From 93c3cbb31be6c8ffae81c18e551cb00c74aaaaf4 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 22 Mar 2015 14:32:51 +0100 Subject: [PATCH] Add basic MCS implementation to realize C2DM (used by GCM) - 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 --- .../org/microg/gms/gcm/mcs/Constants.java | 28 ++ .../microg/gms/gcm/mcs/McsInputStream.java | 127 ++++++ .../microg/gms/gcm/mcs/McsOutputStream.java | 86 ++++ .../org/microg/gms/gcm/mcs/McsService.java | 196 +++++++++ .../org/microg/gms/gcm/mcs/AppData.java | 83 ++++ .../org/microg/gms/gcm/mcs/Close.java | 43 ++ .../microg/gms/gcm/mcs/DataMessageStanza.java | 398 ++++++++++++++++++ .../org/microg/gms/gcm/mcs/ErrorInfo.java | 111 +++++ .../org/microg/gms/gcm/mcs/Extension.java | 93 ++++ .../org/microg/gms/gcm/mcs/HeartbeatAck.java | 99 +++++ .../microg/gms/gcm/mcs/HeartbeatConfig.java | 97 +++++ .../org/microg/gms/gcm/mcs/HeartbeatPing.java | 105 +++++ .../org/microg/gms/gcm/mcs/HeartbeatStat.java | 99 +++++ .../org/microg/gms/gcm/mcs/IqStanza.java | 255 +++++++++++ .../org/microg/gms/gcm/mcs/LoginRequest.java | 367 ++++++++++++++++ .../org/microg/gms/gcm/mcs/LoginResponse.java | 197 +++++++++ .../org/microg/gms/gcm/mcs/SelectiveAck.java | 68 +++ .../org/microg/gms/gcm/mcs/Setting.java | 90 ++++ .../org/microg/gms/gcm/mcs/StreamAck.java | 44 ++ .../microg/gms/gcm/mcs/StreamErrorStanza.java | 83 ++++ .../src/main/protos-repo/mcs.proto | 266 ++++++++++++ 21 files changed, 2935 insertions(+) create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/AppData.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Close.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/DataMessageStanza.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/ErrorInfo.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Extension.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatAck.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatConfig.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatPing.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatStat.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/IqStanza.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginRequest.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginResponse.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/SelectiveAck.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Setting.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamAck.java create mode 100644 play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamErrorStanza.java create mode 100644 play-services-core/src/main/protos-repo/mcs.proto 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 new file mode 100644 index 00000000..27e20ad8 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/Constants.java @@ -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; +} 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 new file mode 100644 index 00000000..825f1d81 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsInputStream.java @@ -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; + } + + +} 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 new file mode 100644 index 00000000..3e719165 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsOutputStream.java @@ -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; + } + } + } +} 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 new file mode 100644 index 00000000..b04c0487 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/mcs/McsService.java @@ -0,0 +1,196 @@ +/* + * 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.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.util.Log; + +import com.squareup.wire.Message; + +import org.microg.gms.checkin.LastCheckinInfo; + +import java.io.IOException; +import java.net.Socket; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.net.ssl.SSLContext; + +import static android.os.Build.VERSION.SDK_INT; + +public class McsService extends IntentService { + private static final String TAG = "GmsGcmMcsSvc"; + public static final String PREFERENCES_NAME = "mcs"; + + public static final String SERVICE_HOST = "mtalk.google.com"; + public static final int SERVICE_PORT = 5228; + public static final String PREF_LAST_PERSISTENT_ID = "last_persistent_id"; + 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"; + private static AtomicBoolean connected = new AtomicBoolean(false); + + private Socket socket; + private Socket sslSocket; + private McsInputStream inputStream; + private McsOutputStream outputStream; + + public McsService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (connected.compareAndSet(false, true)) { + new Thread(new Runnable() { + @Override + public void run() { + connect(); + } + }).start(); + } else { + Log.d(TAG, "MCS connection already started"); + } + } + + private void connect() { + try { + Log.d(TAG, "Starting MCS connection..."); + LastCheckinInfo info = LastCheckinInfo.read(this); + socket = new Socket(SERVICE_HOST, SERVICE_PORT); + Log.d(TAG, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT); + sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, "mtalk.google.com", 5228, true); + Log.d(TAG, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT); + inputStream = new McsInputStream(sslSocket.getInputStream()); + outputStream = new McsOutputStream(sslSocket.getOutputStream()); + LoginRequest loginRequest = buildLoginRequest(info); + Log.d(TAG, "Sending login request..."); + outputStream.write(loginRequest); + boolean close = false; + while (!close) { + Message o = inputStream.read(); + if (o instanceof DataMessageStanza) { + handleMessage((DataMessageStanza) o); + } else if (o instanceof HeartbeatPing) { + handleHearbeatPing((HeartbeatPing) o); + } else if (o instanceof Close) { + handleClose((Close) o); + } else if (o instanceof LoginResponse) { + handleLoginresponse((LoginResponse) o); + } + } + socket.close(); + } catch (Exception e) { + Log.w(TAG, e); + try { + sslSocket.close(); + } catch (Exception ignored) { + } + } + connected.set(false); + } + + private void handleClose(Close close) throws IOException { + throw new IOException("Server requested close!"); + } + + private void handleLoginresponse(LoginResponse loginResponse) throws IOException { + getSharedPreferences().edit().putString(PREF_LAST_PERSISTENT_ID, null); + if (loginResponse.error == null) { + Log.d(TAG, "Logged in"); + } else { + throw new IOException("Could not login: " + loginResponse.error); + } + } + + private void handleMessage(DataMessageStanza message) throws IOException { + if (message.persistent_id != null) { + String old = getSharedPreferences().getString(PREF_LAST_PERSISTENT_ID, null); + if (old == null) { + old = ""; + } else { + old += "|"; + } + getSharedPreferences().edit() + .putString(PREF_LAST_PERSISTENT_ID, old + message.persistent_id).apply(); + } + if (SELF_CATEGORY.equals(message.category)) { + handleSelfMessage(message); + } else { + handleAppMessage(message); + } + } + + private void handleHearbeatPing(HeartbeatPing ping) throws IOException { + HeartbeatAck.Builder ack = new HeartbeatAck.Builder().status(ping.status); + if (inputStream.newStreamIdAvailable()) { + ack.last_stream_id_received(inputStream.getStreamId()); + } + outputStream.write(ack.build()); + } + + private LoginRequest buildLoginRequest(LastCheckinInfo info) { + return new LoginRequest.Builder() + .adaptive_heartbeat(false) + .auth_service(LoginRequest.AuthService.ANDROID_ID) + .auth_token(Long.toString(info.securityToken)) + .id("android-" + SDK_INT) + .domain("mcs.android.com") + .device_id("android-" + Long.toHexString(info.androidId)) + .network_type(1) + .resource(Long.toString(info.androidId)) + .user(Long.toString(info.androidId)) + .use_rmq2(true) + .setting(Arrays.asList(new Setting("new_vc", "1"))) + .received_persistent_id(Arrays.asList(getSharedPreferences().getString(PREF_LAST_PERSISTENT_ID, "").split("\\|"))) + .build(); + } + + private void handleAppMessage(DataMessageStanza msg) { + Intent intent = new Intent(); + intent.setAction("com.google.android.c2dm.intent.RECEIVE"); + intent.addCategory(msg.category); + for (AppData appData : msg.app_data) { + intent.putExtra(appData.key, appData.value); + } + sendOrderedBroadcast(intent, msg.category + ".permission.C2D_MESSAGE"); + } + + private void handleSelfMessage(DataMessageStanza msg) throws IOException { + for (AppData appData : msg.app_data) { + if (IDLE_NOTIFICATION.equals(appData.key)) { + DataMessageStanza.Builder msgResponse = new DataMessageStanza.Builder() + .from(FROM_FIELD) + .sent(System.currentTimeMillis() / 1000) + .ttl(0) + .category(SELF_CATEGORY) + .app_data(Arrays.asList(new AppData(IDLE_NOTIFICATION, "false"))); + if (inputStream.newStreamIdAvailable()) { + msgResponse.last_stream_id_received(inputStream.getStreamId()); + } + outputStream.write(msgResponse.build()); + } + } + } + + private SharedPreferences getSharedPreferences() { + return getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/AppData.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/AppData.java new file mode 100644 index 00000000..61864ae3 --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/AppData.java @@ -0,0 +1,83 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +public final class AppData extends Message { + + public static final String DEFAULT_KEY = ""; + public static final String DEFAULT_VALUE = ""; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String key; + + @ProtoField(tag = 2, type = STRING, label = REQUIRED) + public final String value; + + public AppData(String key, String value) { + this.key = key; + this.value = value; + } + + private AppData(Builder builder) { + this(builder.key, builder.value); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof AppData)) return false; + AppData o = (AppData) other; + return equals(key, o.key) + && equals(value, o.value); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = key != null ? key.hashCode() : 0; + result = result * 37 + (value != null ? value.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String key; + public String value; + + public Builder() { + } + + public Builder(AppData message) { + super(message); + if (message == null) return; + this.key = message.key; + this.value = message.value; + } + + public Builder key(String key) { + this.key = key; + return this; + } + + public Builder value(String value) { + this.value = value; + return this; + } + + @Override + public AppData build() { + checkRequiredFields(); + return new AppData(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Close.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Close.java new file mode 100644 index 00000000..5293b0dd --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Close.java @@ -0,0 +1,43 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; + +/** + * TAG: 4 + */ +public final class Close extends Message { + + public Close() { + } + + private Close(Builder builder) { + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + return other instanceof Close; + } + + @Override + public int hashCode() { + return 0; + } + + public static final class Builder extends Message.Builder { + + public Builder() { + } + + public Builder(Close message) { + super(message); + } + + @Override + public Close build() { + return new Close(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/DataMessageStanza.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/DataMessageStanza.java new file mode 100644 index 00000000..0ce5f17d --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/DataMessageStanza.java @@ -0,0 +1,398 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; + +import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REPEATED; +import static com.squareup.wire.Message.Label.REQUIRED; + +/** + * TAG: 8 + */ +public final class DataMessageStanza extends Message { + + public static final String DEFAULT_ID = ""; + public static final String DEFAULT_FROM = ""; + public static final String DEFAULT_TO = ""; + public static final String DEFAULT_CATEGORY = ""; + public static final String DEFAULT_TOKEN = ""; + public static final List DEFAULT_APP_DATA = Collections.emptyList(); + public static final Boolean DEFAULT_FROM_TRUSTED_SERVER = false; + public static final String DEFAULT_PERSISTENT_ID = ""; + public static final Integer DEFAULT_STREAM_ID = 0; + public static final Integer DEFAULT_LAST_STREAM_ID_RECEIVED = 0; + public static final String DEFAULT_REG_ID = ""; + public static final Long DEFAULT_DEVICE_USER_ID = 0L; + public static final Integer DEFAULT_TTL = 0; + public static final Long DEFAULT_SENT = 0L; + public static final Integer DEFAULT_QUEUED = 0; + public static final Long DEFAULT_STATUS = 0L; + + /** + * Not used. + * optional int64 rmq_id = 1; + * This is the message ID, set by client, DMP.9 (message_id) + */ + @ProtoField(tag = 2, type = STRING) + public final String id; + + /** + * Project ID of the sender, DMP.1 + */ + @ProtoField(tag = 3, type = STRING, label = REQUIRED) + public final String from; + + /** + * Part of DMRequest - also the key in DataMessageProto. + */ + @ProtoField(tag = 4, type = STRING) + public final String to; + + /** + * Package name. DMP.2 + */ + @ProtoField(tag = 5, type = STRING, label = REQUIRED) + public final String category; + + /** + * The collapsed key, DMP.3 + */ + @ProtoField(tag = 6, type = STRING) + public final String token; + + /** + * User data + GOOGLE. prefixed special entries, DMP.4 + */ + @ProtoField(tag = 7, label = REPEATED, messageType = AppData.class) + public final List app_data; + + /** + * Not used. + */ + @ProtoField(tag = 8, type = BOOL) + public final Boolean from_trusted_server; + + /** + * Part of the ACK protocol, returned in DataMessageResponse on server side. + * It's part of the key of DMP. + */ + @ProtoField(tag = 9, type = STRING) + public final String persistent_id; + + /** + * In-stream ack. Increments on each message sent - a bit redundant + * Not used in DMP/DMR. + */ + @ProtoField(tag = 10, type = INT32) + public final Integer stream_id; + + @ProtoField(tag = 11, type = INT32) + public final Integer last_stream_id_received; + + /** + * Not used. + * optional string permission = 12; + * Sent by the device shortly after registration. + */ + @ProtoField(tag = 13, type = STRING) + public final String reg_id; + + /** + * Not used. + * optional string pkg_signature = 14; + * Not used. + * optional string client_id = 15; + * serial number of the target user, DMP.8 + * It is the 'serial number' according to user manager. + */ + @ProtoField(tag = 16, type = INT64) + public final Long device_user_id; + + /** + * Time to live, in seconds. + */ + @ProtoField(tag = 17, type = INT32) + public final Integer ttl; + + /** + * Timestamp ( according to client ) when message was sent by app, in seconds + */ + @ProtoField(tag = 18, type = INT64) + public final Long sent; + + /** + * How long has the message been queued before the flush, in seconds. + * This is needed to account for the time difference between server and + * client: server should adjust 'sent' based on his 'receive' time. + */ + @ProtoField(tag = 19, type = INT32) + public final Integer queued; + + @ProtoField(tag = 20, type = INT64) + public final Long status; + + public DataMessageStanza(String id, String from, String to, String category, String token, List app_data, Boolean from_trusted_server, String persistent_id, Integer stream_id, Integer last_stream_id_received, String reg_id, Long device_user_id, Integer ttl, Long sent, Integer queued, Long status) { + this.id = id; + this.from = from; + this.to = to; + this.category = category; + this.token = token; + this.app_data = immutableCopyOf(app_data); + this.from_trusted_server = from_trusted_server; + this.persistent_id = persistent_id; + this.stream_id = stream_id; + this.last_stream_id_received = last_stream_id_received; + this.reg_id = reg_id; + this.device_user_id = device_user_id; + this.ttl = ttl; + this.sent = sent; + this.queued = queued; + this.status = status; + } + + private DataMessageStanza(Builder builder) { + this(builder.id, builder.from, builder.to, builder.category, builder.token, builder.app_data, builder.from_trusted_server, builder.persistent_id, builder.stream_id, builder.last_stream_id_received, builder.reg_id, builder.device_user_id, builder.ttl, builder.sent, builder.queued, builder.status); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof DataMessageStanza)) return false; + DataMessageStanza o = (DataMessageStanza) other; + return equals(id, o.id) + && equals(from, o.from) + && equals(to, o.to) + && equals(category, o.category) + && equals(token, o.token) + && equals(app_data, o.app_data) + && equals(from_trusted_server, o.from_trusted_server) + && equals(persistent_id, o.persistent_id) + && equals(stream_id, o.stream_id) + && equals(last_stream_id_received, o.last_stream_id_received) + && equals(reg_id, o.reg_id) + && equals(device_user_id, o.device_user_id) + && equals(ttl, o.ttl) + && equals(sent, o.sent) + && equals(queued, o.queued) + && equals(status, o.status); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = id != null ? id.hashCode() : 0; + result = result * 37 + (from != null ? from.hashCode() : 0); + result = result * 37 + (to != null ? to.hashCode() : 0); + result = result * 37 + (category != null ? category.hashCode() : 0); + result = result * 37 + (token != null ? token.hashCode() : 0); + result = result * 37 + (app_data != null ? app_data.hashCode() : 1); + result = result * 37 + (from_trusted_server != null ? from_trusted_server.hashCode() : 0); + result = result * 37 + (persistent_id != null ? persistent_id.hashCode() : 0); + result = result * 37 + (stream_id != null ? stream_id.hashCode() : 0); + result = result * 37 + (last_stream_id_received != null ? last_stream_id_received.hashCode() : 0); + result = result * 37 + (reg_id != null ? reg_id.hashCode() : 0); + result = result * 37 + (device_user_id != null ? device_user_id.hashCode() : 0); + result = result * 37 + (ttl != null ? ttl.hashCode() : 0); + result = result * 37 + (sent != null ? sent.hashCode() : 0); + result = result * 37 + (queued != null ? queued.hashCode() : 0); + result = result * 37 + (status != null ? status.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String id; + public String from; + public String to; + public String category; + public String token; + public List app_data; + public Boolean from_trusted_server; + public String persistent_id; + public Integer stream_id; + public Integer last_stream_id_received; + public String reg_id; + public Long device_user_id; + public Integer ttl; + public Long sent; + public Integer queued; + public Long status; + + public Builder() { + } + + public Builder(DataMessageStanza message) { + super(message); + if (message == null) return; + this.id = message.id; + this.from = message.from; + this.to = message.to; + this.category = message.category; + this.token = message.token; + this.app_data = copyOf(message.app_data); + this.from_trusted_server = message.from_trusted_server; + this.persistent_id = message.persistent_id; + this.stream_id = message.stream_id; + this.last_stream_id_received = message.last_stream_id_received; + this.reg_id = message.reg_id; + this.device_user_id = message.device_user_id; + this.ttl = message.ttl; + this.sent = message.sent; + this.queued = message.queued; + this.status = message.status; + } + + /** + * Not used. + * optional int64 rmq_id = 1; + * This is the message ID, set by client, DMP.9 (message_id) + */ + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Project ID of the sender, DMP.1 + */ + public Builder from(String from) { + this.from = from; + return this; + } + + /** + * Part of DMRequest - also the key in DataMessageProto. + */ + public Builder to(String to) { + this.to = to; + return this; + } + + /** + * Package name. DMP.2 + */ + public Builder category(String category) { + this.category = category; + return this; + } + + /** + * The collapsed key, DMP.3 + */ + public Builder token(String token) { + this.token = token; + return this; + } + + /** + * User data + GOOGLE. prefixed special entries, DMP.4 + */ + public Builder app_data(List app_data) { + this.app_data = checkForNulls(app_data); + return this; + } + + /** + * Not used. + */ + public Builder from_trusted_server(Boolean from_trusted_server) { + this.from_trusted_server = from_trusted_server; + return this; + } + + /** + * Part of the ACK protocol, returned in DataMessageResponse on server side. + * It's part of the key of DMP. + */ + public Builder persistent_id(String persistent_id) { + this.persistent_id = persistent_id; + return this; + } + + /** + * In-stream ack. Increments on each message sent - a bit redundant + * Not used in DMP/DMR. + */ + public Builder stream_id(Integer stream_id) { + this.stream_id = stream_id; + return this; + } + + public Builder last_stream_id_received(Integer last_stream_id_received) { + this.last_stream_id_received = last_stream_id_received; + return this; + } + + /** + * Not used. + * optional string permission = 12; + * Sent by the device shortly after registration. + */ + public Builder reg_id(String reg_id) { + this.reg_id = reg_id; + return this; + } + + /** + * Not used. + * optional string pkg_signature = 14; + * Not used. + * optional string client_id = 15; + * serial number of the target user, DMP.8 + * It is the 'serial number' according to user manager. + */ + public Builder device_user_id(Long device_user_id) { + this.device_user_id = device_user_id; + return this; + } + + /** + * Time to live, in seconds. + */ + public Builder ttl(Integer ttl) { + this.ttl = ttl; + return this; + } + + /** + * Timestamp ( according to client ) when message was sent by app, in seconds + */ + public Builder sent(Long sent) { + this.sent = sent; + return this; + } + + /** + * How long has the message been queued before the flush, in seconds. + * This is needed to account for the time difference between server and + * client: server should adjust 'sent' based on his 'receive' time. + */ + public Builder queued(Integer queued) { + this.queued = queued; + return this; + } + + public Builder status(Long status) { + this.status = status; + return this; + } + + @Override + public DataMessageStanza build() { + checkRequiredFields(); + return new DataMessageStanza(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/ErrorInfo.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/ErrorInfo.java new file mode 100644 index 00000000..694a8768 --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/ErrorInfo.java @@ -0,0 +1,111 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +public final class ErrorInfo extends Message { + + public static final Integer DEFAULT_CODE = 0; + public static final String DEFAULT_MESSAGE = ""; + public static final String DEFAULT_TYPE = ""; + + @ProtoField(tag = 1, type = INT32, label = REQUIRED) + public final Integer code; + + @ProtoField(tag = 2, type = STRING) + public final String message; + + @ProtoField(tag = 3, type = STRING) + public final String type; + + @ProtoField(tag = 4) + public final Extension extension; + + public ErrorInfo(Integer code, String message, String type, Extension extension) { + this.code = code; + this.message = message; + this.type = type; + this.extension = extension; + } + + private ErrorInfo(Builder builder) { + this(builder.code, builder.message, builder.type, builder.extension); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof ErrorInfo)) return false; + ErrorInfo o = (ErrorInfo) other; + return equals(code, o.code) + && equals(message, o.message) + && equals(type, o.type) + && equals(extension, o.extension); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = code != null ? code.hashCode() : 0; + result = result * 37 + (message != null ? message.hashCode() : 0); + result = result * 37 + (type != null ? type.hashCode() : 0); + result = result * 37 + (extension != null ? extension.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Integer code; + public String message; + public String type; + public Extension extension; + + public Builder() { + } + + public Builder(ErrorInfo message) { + super(message); + if (message == null) return; + this.code = message.code; + this.message = message.message; + this.type = message.type; + this.extension = message.extension; + } + + public Builder code(Integer code) { + this.code = code; + return this; + } + + public Builder message(String message) { + this.message = message; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder extension(Extension extension) { + this.extension = extension; + return this; + } + + @Override + public ErrorInfo build() { + checkRequiredFields(); + return new ErrorInfo(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Extension.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Extension.java new file mode 100644 index 00000000..21b87ea7 --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Extension.java @@ -0,0 +1,93 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import okio.ByteString; + +import static com.squareup.wire.Message.Datatype.BYTES; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Label.REQUIRED; + +public final class Extension extends Message { + + public static final Integer DEFAULT_ID = 0; + public static final ByteString DEFAULT_DATA = ByteString.EMPTY; + + /** + * 12: SelectiveAck + * 13: StreamAck + */ + @ProtoField(tag = 1, type = INT32, label = REQUIRED) + public final Integer id; + + @ProtoField(tag = 2, type = BYTES, label = REQUIRED) + public final ByteString data; + + public Extension(Integer id, ByteString data) { + this.id = id; + this.data = data; + } + + private Extension(Builder builder) { + this(builder.id, builder.data); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof Extension)) return false; + Extension o = (Extension) other; + return equals(id, o.id) + && equals(data, o.data); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = id != null ? id.hashCode() : 0; + result = result * 37 + (data != null ? data.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Integer id; + public ByteString data; + + public Builder() { + } + + public Builder(Extension message) { + super(message); + if (message == null) return; + this.id = message.id; + this.data = message.data; + } + + /** + * 12: SelectiveAck + * 13: StreamAck + */ + public Builder id(Integer id) { + this.id = id; + return this; + } + + public Builder data(ByteString data) { + this.data = data; + return this; + } + + @Override + public Extension build() { + checkRequiredFields(); + return new Extension(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatAck.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatAck.java new file mode 100644 index 00000000..29c8564d --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatAck.java @@ -0,0 +1,99 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; + +/** + * TAG: 1 + */ +public final class HeartbeatAck extends Message { + + public static final Integer DEFAULT_STREAM_ID = 0; + public static final Integer DEFAULT_LAST_STREAM_ID_RECEIVED = 0; + public static final Long DEFAULT_STATUS = 0L; + + @ProtoField(tag = 1, type = INT32) + public final Integer stream_id; + + @ProtoField(tag = 2, type = INT32) + public final Integer last_stream_id_received; + + @ProtoField(tag = 3, type = INT64) + public final Long status; + + public HeartbeatAck(Integer stream_id, Integer last_stream_id_received, Long status) { + this.stream_id = stream_id; + this.last_stream_id_received = last_stream_id_received; + this.status = status; + } + + private HeartbeatAck(Builder builder) { + this(builder.stream_id, builder.last_stream_id_received, builder.status); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof HeartbeatAck)) return false; + HeartbeatAck o = (HeartbeatAck) other; + return equals(stream_id, o.stream_id) + && equals(last_stream_id_received, o.last_stream_id_received) + && equals(status, o.status); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = stream_id != null ? stream_id.hashCode() : 0; + result = result * 37 + (last_stream_id_received != null ? last_stream_id_received.hashCode() : 0); + result = result * 37 + (status != null ? status.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Integer stream_id; + public Integer last_stream_id_received; + public Long status; + + public Builder() { + } + + public Builder(HeartbeatAck message) { + super(message); + if (message == null) return; + this.stream_id = message.stream_id; + this.last_stream_id_received = message.last_stream_id_received; + this.status = message.status; + } + + public Builder stream_id(Integer stream_id) { + this.stream_id = stream_id; + return this; + } + + public Builder last_stream_id_received(Integer last_stream_id_received) { + this.last_stream_id_received = last_stream_id_received; + return this; + } + + public Builder status(Long status) { + this.status = status; + return this; + } + + @Override + public HeartbeatAck build() { + return new HeartbeatAck(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatConfig.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatConfig.java new file mode 100644 index 00000000..bc36f87f --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatConfig.java @@ -0,0 +1,97 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.STRING; + +public final class HeartbeatConfig extends Message { + + public static final Boolean DEFAULT_UPLOAD_STAT = false; + public static final String DEFAULT_IP = ""; + public static final Integer DEFAULT_INTERVAL_MS = 0; + + @ProtoField(tag = 1, type = BOOL) + public final Boolean upload_stat; + + @ProtoField(tag = 2, type = STRING) + public final String ip; + + @ProtoField(tag = 3, type = INT32) + public final Integer interval_ms; + + public HeartbeatConfig(Boolean upload_stat, String ip, Integer interval_ms) { + this.upload_stat = upload_stat; + this.ip = ip; + this.interval_ms = interval_ms; + } + + private HeartbeatConfig(Builder builder) { + this(builder.upload_stat, builder.ip, builder.interval_ms); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof HeartbeatConfig)) return false; + HeartbeatConfig o = (HeartbeatConfig) other; + return equals(upload_stat, o.upload_stat) + && equals(ip, o.ip) + && equals(interval_ms, o.interval_ms); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = upload_stat != null ? upload_stat.hashCode() : 0; + result = result * 37 + (ip != null ? ip.hashCode() : 0); + result = result * 37 + (interval_ms != null ? interval_ms.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Boolean upload_stat; + public String ip; + public Integer interval_ms; + + public Builder() { + } + + public Builder(HeartbeatConfig message) { + super(message); + if (message == null) return; + this.upload_stat = message.upload_stat; + this.ip = message.ip; + this.interval_ms = message.interval_ms; + } + + public Builder upload_stat(Boolean upload_stat) { + this.upload_stat = upload_stat; + return this; + } + + public Builder ip(String ip) { + this.ip = ip; + return this; + } + + public Builder interval_ms(Integer interval_ms) { + this.interval_ms = interval_ms; + return this; + } + + @Override + public HeartbeatConfig build() { + return new HeartbeatConfig(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatPing.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatPing.java new file mode 100644 index 00000000..c203234a --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatPing.java @@ -0,0 +1,105 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; + +/** + * Common fields/comments: + * + * stream_id: no longer sent by server, each side keeps a counter + * last_stream_id_received: sent only if a packet was received since last time + * a last_stream was sent + * status: new bitmask including the 'idle' as bit 0. + * TAG: 0 + */ +public final class HeartbeatPing extends Message { + + public static final Integer DEFAULT_STREAM_ID = 0; + public static final Integer DEFAULT_LAST_STREAM_ID_RECEIVED = 0; + public static final Long DEFAULT_STATUS = 0L; + + @ProtoField(tag = 1, type = INT32) + public final Integer stream_id; + + @ProtoField(tag = 2, type = INT32) + public final Integer last_stream_id_received; + + @ProtoField(tag = 3, type = INT64) + public final Long status; + + public HeartbeatPing(Integer stream_id, Integer last_stream_id_received, Long status) { + this.stream_id = stream_id; + this.last_stream_id_received = last_stream_id_received; + this.status = status; + } + + private HeartbeatPing(Builder builder) { + this(builder.stream_id, builder.last_stream_id_received, builder.status); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof HeartbeatPing)) return false; + HeartbeatPing o = (HeartbeatPing) other; + return equals(stream_id, o.stream_id) + && equals(last_stream_id_received, o.last_stream_id_received) + && equals(status, o.status); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = stream_id != null ? stream_id.hashCode() : 0; + result = result * 37 + (last_stream_id_received != null ? last_stream_id_received.hashCode() : 0); + result = result * 37 + (status != null ? status.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Integer stream_id; + public Integer last_stream_id_received; + public Long status; + + public Builder() { + } + + public Builder(HeartbeatPing message) { + super(message); + if (message == null) return; + this.stream_id = message.stream_id; + this.last_stream_id_received = message.last_stream_id_received; + this.status = message.status; + } + + public Builder stream_id(Integer stream_id) { + this.stream_id = stream_id; + return this; + } + + public Builder last_stream_id_received(Integer last_stream_id_received) { + this.last_stream_id_received = last_stream_id_received; + return this; + } + + public Builder status(Long status) { + this.status = status; + return this; + } + + @Override + public HeartbeatPing build() { + return new HeartbeatPing(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatStat.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatStat.java new file mode 100644 index 00000000..2e504218 --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/HeartbeatStat.java @@ -0,0 +1,99 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +public final class HeartbeatStat extends Message { + + public static final String DEFAULT_IP = ""; + public static final Boolean DEFAULT_TIMEOUT = false; + public static final Integer DEFAULT_INTERVAL_MS = 0; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String ip; + + @ProtoField(tag = 2, type = BOOL, label = REQUIRED) + public final Boolean timeout; + + @ProtoField(tag = 3, type = INT32, label = REQUIRED) + public final Integer interval_ms; + + public HeartbeatStat(String ip, Boolean timeout, Integer interval_ms) { + this.ip = ip; + this.timeout = timeout; + this.interval_ms = interval_ms; + } + + private HeartbeatStat(Builder builder) { + this(builder.ip, builder.timeout, builder.interval_ms); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof HeartbeatStat)) return false; + HeartbeatStat o = (HeartbeatStat) other; + return equals(ip, o.ip) + && equals(timeout, o.timeout) + && equals(interval_ms, o.interval_ms); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = ip != null ? ip.hashCode() : 0; + result = result * 37 + (timeout != null ? timeout.hashCode() : 0); + result = result * 37 + (interval_ms != null ? interval_ms.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String ip; + public Boolean timeout; + public Integer interval_ms; + + public Builder() { + } + + public Builder(HeartbeatStat message) { + super(message); + if (message == null) return; + this.ip = message.ip; + this.timeout = message.timeout; + this.interval_ms = message.interval_ms; + } + + public Builder ip(String ip) { + this.ip = ip; + return this; + } + + public Builder timeout(Boolean timeout) { + this.timeout = timeout; + return this; + } + + public Builder interval_ms(Integer interval_ms) { + this.interval_ms = interval_ms; + return this; + } + + @Override + public HeartbeatStat build() { + checkRequiredFields(); + return new HeartbeatStat(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/IqStanza.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/IqStanza.java new file mode 100644 index 00000000..b881f7bc --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/IqStanza.java @@ -0,0 +1,255 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoEnum; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.ENUM; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +/** + * TAG: 7 + * IqRequest must contain a single extension. IqResponse may contain 0 or 1 + * extensions. + */ +public final class IqStanza extends Message { + + public static final Long DEFAULT_RMQ_ID = 0L; + public static final IqType DEFAULT_TYPE = IqType.GET; + public static final String DEFAULT_ID = ""; + public static final String DEFAULT_FROM = ""; + public static final String DEFAULT_TO = ""; + public static final String DEFAULT_PERSISTENT_ID = ""; + public static final Integer DEFAULT_STREAM_ID = 0; + public static final Integer DEFAULT_LAST_STREAM_ID_RECEIVED = 0; + public static final Long DEFAULT_ACCOUNT_ID = 0L; + public static final Long DEFAULT_STATUS = 0L; + + @ProtoField(tag = 1, type = INT64) + public final Long rmq_id; + + @ProtoField(tag = 2, type = ENUM, label = REQUIRED) + public final IqType type; + + @ProtoField(tag = 3, type = STRING, label = REQUIRED) + public final String id; + + @ProtoField(tag = 4, type = STRING) + public final String from; + + @ProtoField(tag = 5, type = STRING) + public final String to; + + @ProtoField(tag = 6) + public final ErrorInfo error; + + /** + * Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id) + */ + @ProtoField(tag = 7) + public final Extension extension; + + @ProtoField(tag = 8, type = STRING) + public final String persistent_id; + + @ProtoField(tag = 9, type = INT32) + public final Integer stream_id; + + @ProtoField(tag = 10, type = INT32) + public final Integer last_stream_id_received; + + @ProtoField(tag = 11, type = INT64) + public final Long account_id; + + @ProtoField(tag = 12, type = INT64) + public final Long status; + + public IqStanza(Long rmq_id, IqType type, String id, String from, String to, ErrorInfo error, Extension extension, String persistent_id, Integer stream_id, Integer last_stream_id_received, Long account_id, Long status) { + this.rmq_id = rmq_id; + this.type = type; + this.id = id; + this.from = from; + this.to = to; + this.error = error; + this.extension = extension; + this.persistent_id = persistent_id; + this.stream_id = stream_id; + this.last_stream_id_received = last_stream_id_received; + this.account_id = account_id; + this.status = status; + } + + private IqStanza(Builder builder) { + this(builder.rmq_id, builder.type, builder.id, builder.from, builder.to, builder.error, builder.extension, builder.persistent_id, builder.stream_id, builder.last_stream_id_received, builder.account_id, builder.status); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof IqStanza)) return false; + IqStanza o = (IqStanza) other; + return equals(rmq_id, o.rmq_id) + && equals(type, o.type) + && equals(id, o.id) + && equals(from, o.from) + && equals(to, o.to) + && equals(error, o.error) + && equals(extension, o.extension) + && equals(persistent_id, o.persistent_id) + && equals(stream_id, o.stream_id) + && equals(last_stream_id_received, o.last_stream_id_received) + && equals(account_id, o.account_id) + && equals(status, o.status); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = rmq_id != null ? rmq_id.hashCode() : 0; + result = result * 37 + (type != null ? type.hashCode() : 0); + result = result * 37 + (id != null ? id.hashCode() : 0); + result = result * 37 + (from != null ? from.hashCode() : 0); + result = result * 37 + (to != null ? to.hashCode() : 0); + result = result * 37 + (error != null ? error.hashCode() : 0); + result = result * 37 + (extension != null ? extension.hashCode() : 0); + result = result * 37 + (persistent_id != null ? persistent_id.hashCode() : 0); + result = result * 37 + (stream_id != null ? stream_id.hashCode() : 0); + result = result * 37 + (last_stream_id_received != null ? last_stream_id_received.hashCode() : 0); + result = result * 37 + (account_id != null ? account_id.hashCode() : 0); + result = result * 37 + (status != null ? status.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public Long rmq_id; + public IqType type; + public String id; + public String from; + public String to; + public ErrorInfo error; + public Extension extension; + public String persistent_id; + public Integer stream_id; + public Integer last_stream_id_received; + public Long account_id; + public Long status; + + public Builder() { + } + + public Builder(IqStanza message) { + super(message); + if (message == null) return; + this.rmq_id = message.rmq_id; + this.type = message.type; + this.id = message.id; + this.from = message.from; + this.to = message.to; + this.error = message.error; + this.extension = message.extension; + this.persistent_id = message.persistent_id; + this.stream_id = message.stream_id; + this.last_stream_id_received = message.last_stream_id_received; + this.account_id = message.account_id; + this.status = message.status; + } + + public Builder rmq_id(Long rmq_id) { + this.rmq_id = rmq_id; + return this; + } + + public Builder type(IqType type) { + this.type = type; + return this; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + public Builder from(String from) { + this.from = from; + return this; + } + + public Builder to(String to) { + this.to = to; + return this; + } + + public Builder error(ErrorInfo error) { + this.error = error; + return this; + } + + /** + * Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id) + */ + public Builder extension(Extension extension) { + this.extension = extension; + return this; + } + + public Builder persistent_id(String persistent_id) { + this.persistent_id = persistent_id; + return this; + } + + public Builder stream_id(Integer stream_id) { + this.stream_id = stream_id; + return this; + } + + public Builder last_stream_id_received(Integer last_stream_id_received) { + this.last_stream_id_received = last_stream_id_received; + return this; + } + + public Builder account_id(Long account_id) { + this.account_id = account_id; + return this; + } + + public Builder status(Long status) { + this.status = status; + return this; + } + + @Override + public IqStanza build() { + checkRequiredFields(); + return new IqStanza(this); + } + } + + public enum IqType + implements ProtoEnum { + GET(0), + SET(1), + RESULT(2), + IQ_ERROR(3); + + private final int value; + + private IqType(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginRequest.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginRequest.java new file mode 100644 index 00000000..18c21261 --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginRequest.java @@ -0,0 +1,367 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoEnum; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; + +import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.ENUM; +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REPEATED; +import static com.squareup.wire.Message.Label.REQUIRED; + +/** + * TAG: 2 + */ +public final class LoginRequest extends Message { + + public static final String DEFAULT_ID = ""; + public static final String DEFAULT_DOMAIN = ""; + public static final String DEFAULT_USER = ""; + public static final String DEFAULT_RESOURCE = ""; + public static final String DEFAULT_AUTH_TOKEN = ""; + public static final String DEFAULT_DEVICE_ID = ""; + public static final Long DEFAULT_LAST_RMQ_ID = 0L; + public static final List DEFAULT_SETTING = Collections.emptyList(); + public static final List DEFAULT_RECEIVED_PERSISTENT_ID = Collections.emptyList(); + public static final Boolean DEFAULT_ADAPTIVE_HEARTBEAT = false; + public static final Boolean DEFAULT_USE_RMQ2 = false; + public static final Long DEFAULT_ACCOUNT_ID = 0L; + public static final AuthService DEFAULT_AUTH_SERVICE = AuthService.ANDROID_ID; + public static final Integer DEFAULT_NETWORK_TYPE = 0; + public static final Long DEFAULT_STATUS = 0L; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String id; + + /** + * Must be present ( proto required ), may be empty + * string. + * mcs.android.com. + */ + @ProtoField(tag = 2, type = STRING, label = REQUIRED) + public final String domain; + + /** + * Decimal android ID + */ + @ProtoField(tag = 3, type = STRING, label = REQUIRED) + public final String user; + + @ProtoField(tag = 4, type = STRING, label = REQUIRED) + public final String resource; + + /** + * Secret + */ + @ProtoField(tag = 5, type = STRING, label = REQUIRED) + public final String auth_token; + + /** + * Format is: android-HEX_DEVICE_ID + * The user is the decimal value. + */ + @ProtoField(tag = 6, type = STRING) + public final String device_id; + + /** + * RMQ1 - no longer used + */ + @ProtoField(tag = 7, type = INT64) + public final Long last_rmq_id; + + @ProtoField(tag = 8, label = REPEATED, messageType = Setting.class) + public final List setting; + + /** + * optional int32 compress = 9; + */ + @ProtoField(tag = 10, type = STRING, label = REPEATED) + public final List received_persistent_id; + + /** + * Replaced by "rmq2v" setting + * optional bool include_stream_ids = 11; + */ + @ProtoField(tag = 12, type = BOOL) + public final Boolean adaptive_heartbeat; + + @ProtoField(tag = 13) + public final HeartbeatStat heartbeat_stat; + + /** + * Must be true. + */ + @ProtoField(tag = 14, type = BOOL) + public final Boolean use_rmq2; + + @ProtoField(tag = 15, type = INT64) + public final Long account_id; + + /** + * ANDROID_ID = 2 + */ + @ProtoField(tag = 16, type = ENUM) + public final AuthService auth_service; + + @ProtoField(tag = 17, type = INT32) + public final Integer network_type; + + @ProtoField(tag = 18, type = INT64) + public final Long status; + + public LoginRequest(String id, String domain, String user, String resource, String auth_token, String device_id, Long last_rmq_id, List setting, List received_persistent_id, Boolean adaptive_heartbeat, HeartbeatStat heartbeat_stat, Boolean use_rmq2, Long account_id, AuthService auth_service, Integer network_type, Long status) { + this.id = id; + this.domain = domain; + this.user = user; + this.resource = resource; + this.auth_token = auth_token; + this.device_id = device_id; + this.last_rmq_id = last_rmq_id; + this.setting = immutableCopyOf(setting); + this.received_persistent_id = immutableCopyOf(received_persistent_id); + this.adaptive_heartbeat = adaptive_heartbeat; + this.heartbeat_stat = heartbeat_stat; + this.use_rmq2 = use_rmq2; + this.account_id = account_id; + this.auth_service = auth_service; + this.network_type = network_type; + this.status = status; + } + + private LoginRequest(Builder builder) { + this(builder.id, builder.domain, builder.user, builder.resource, builder.auth_token, builder.device_id, builder.last_rmq_id, builder.setting, builder.received_persistent_id, builder.adaptive_heartbeat, builder.heartbeat_stat, builder.use_rmq2, builder.account_id, builder.auth_service, builder.network_type, builder.status); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof LoginRequest)) return false; + LoginRequest o = (LoginRequest) other; + return equals(id, o.id) + && equals(domain, o.domain) + && equals(user, o.user) + && equals(resource, o.resource) + && equals(auth_token, o.auth_token) + && equals(device_id, o.device_id) + && equals(last_rmq_id, o.last_rmq_id) + && equals(setting, o.setting) + && equals(received_persistent_id, o.received_persistent_id) + && equals(adaptive_heartbeat, o.adaptive_heartbeat) + && equals(heartbeat_stat, o.heartbeat_stat) + && equals(use_rmq2, o.use_rmq2) + && equals(account_id, o.account_id) + && equals(auth_service, o.auth_service) + && equals(network_type, o.network_type) + && equals(status, o.status); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = id != null ? id.hashCode() : 0; + result = result * 37 + (domain != null ? domain.hashCode() : 0); + result = result * 37 + (user != null ? user.hashCode() : 0); + result = result * 37 + (resource != null ? resource.hashCode() : 0); + result = result * 37 + (auth_token != null ? auth_token.hashCode() : 0); + result = result * 37 + (device_id != null ? device_id.hashCode() : 0); + result = result * 37 + (last_rmq_id != null ? last_rmq_id.hashCode() : 0); + result = result * 37 + (setting != null ? setting.hashCode() : 1); + result = result * 37 + (received_persistent_id != null ? received_persistent_id.hashCode() : 1); + result = result * 37 + (adaptive_heartbeat != null ? adaptive_heartbeat.hashCode() : 0); + result = result * 37 + (heartbeat_stat != null ? heartbeat_stat.hashCode() : 0); + result = result * 37 + (use_rmq2 != null ? use_rmq2.hashCode() : 0); + result = result * 37 + (account_id != null ? account_id.hashCode() : 0); + result = result * 37 + (auth_service != null ? auth_service.hashCode() : 0); + result = result * 37 + (network_type != null ? network_type.hashCode() : 0); + result = result * 37 + (status != null ? status.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String id; + public String domain; + public String user; + public String resource; + public String auth_token; + public String device_id; + public Long last_rmq_id; + public List setting; + public List received_persistent_id; + public Boolean adaptive_heartbeat; + public HeartbeatStat heartbeat_stat; + public Boolean use_rmq2; + public Long account_id; + public AuthService auth_service; + public Integer network_type; + public Long status; + + public Builder() { + } + + public Builder(LoginRequest message) { + super(message); + if (message == null) return; + this.id = message.id; + this.domain = message.domain; + this.user = message.user; + this.resource = message.resource; + this.auth_token = message.auth_token; + this.device_id = message.device_id; + this.last_rmq_id = message.last_rmq_id; + this.setting = copyOf(message.setting); + this.received_persistent_id = copyOf(message.received_persistent_id); + this.adaptive_heartbeat = message.adaptive_heartbeat; + this.heartbeat_stat = message.heartbeat_stat; + this.use_rmq2 = message.use_rmq2; + this.account_id = message.account_id; + this.auth_service = message.auth_service; + this.network_type = message.network_type; + this.status = message.status; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Must be present ( proto required ), may be empty + * string. + * mcs.android.com. + */ + public Builder domain(String domain) { + this.domain = domain; + return this; + } + + /** + * Decimal android ID + */ + public Builder user(String user) { + this.user = user; + return this; + } + + public Builder resource(String resource) { + this.resource = resource; + return this; + } + + /** + * Secret + */ + public Builder auth_token(String auth_token) { + this.auth_token = auth_token; + return this; + } + + /** + * Format is: android-HEX_DEVICE_ID + * The user is the decimal value. + */ + public Builder device_id(String device_id) { + this.device_id = device_id; + return this; + } + + /** + * RMQ1 - no longer used + */ + public Builder last_rmq_id(Long last_rmq_id) { + this.last_rmq_id = last_rmq_id; + return this; + } + + public Builder setting(List setting) { + this.setting = checkForNulls(setting); + return this; + } + + /** + * optional int32 compress = 9; + */ + public Builder received_persistent_id(List received_persistent_id) { + this.received_persistent_id = checkForNulls(received_persistent_id); + return this; + } + + /** + * Replaced by "rmq2v" setting + * optional bool include_stream_ids = 11; + */ + public Builder adaptive_heartbeat(Boolean adaptive_heartbeat) { + this.adaptive_heartbeat = adaptive_heartbeat; + return this; + } + + public Builder heartbeat_stat(HeartbeatStat heartbeat_stat) { + this.heartbeat_stat = heartbeat_stat; + return this; + } + + /** + * Must be true. + */ + public Builder use_rmq2(Boolean use_rmq2) { + this.use_rmq2 = use_rmq2; + return this; + } + + public Builder account_id(Long account_id) { + this.account_id = account_id; + return this; + } + + /** + * ANDROID_ID = 2 + */ + public Builder auth_service(AuthService auth_service) { + this.auth_service = auth_service; + return this; + } + + public Builder network_type(Integer network_type) { + this.network_type = network_type; + return this; + } + + public Builder status(Long status) { + this.status = status; + return this; + } + + @Override + public LoginRequest build() { + checkRequiredFields(); + return new LoginRequest(this); + } + } + + public enum AuthService + implements ProtoEnum { + ANDROID_ID(2); + + private final int value; + + private AuthService(int value) { + this.value = value; + } + + @Override + public int getValue() { + return value; + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginResponse.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginResponse.java new file mode 100644 index 00000000..3ffebafe --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/LoginResponse.java @@ -0,0 +1,197 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; + +import static com.squareup.wire.Message.Datatype.INT32; +import static com.squareup.wire.Message.Datatype.INT64; +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REPEATED; +import static com.squareup.wire.Message.Label.REQUIRED; + +/** + * TAG: 3 + */ +public final class LoginResponse extends Message { + + public static final String DEFAULT_ID = ""; + public static final String DEFAULT_JID = ""; + public static final List DEFAULT_SETTING = Collections.emptyList(); + public static final Integer DEFAULT_STREAM_ID = 0; + public static final Integer DEFAULT_LAST_STREAM_ID_RECEIVED = 0; + public static final Long DEFAULT_SERVER_TIMESTAMP = 0L; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String id; + + /** + * Not used. + */ + @ProtoField(tag = 2, type = STRING) + public final String jid; + + /** + * Null if login was ok. + */ + @ProtoField(tag = 3) + public final ErrorInfo error; + + @ProtoField(tag = 4, label = REPEATED, messageType = Setting.class) + public final List setting; + + @ProtoField(tag = 5, type = INT32) + public final Integer stream_id; + + /** + * Should be "1" + */ + @ProtoField(tag = 6, type = INT32) + public final Integer last_stream_id_received; + + @ProtoField(tag = 7) + public final HeartbeatConfig heartbeat_config; + + /** + * used by the client to synchronize with the server timestamp. + */ + @ProtoField(tag = 8, type = INT64) + public final Long server_timestamp; + + public LoginResponse(String id, String jid, ErrorInfo error, List setting, Integer stream_id, Integer last_stream_id_received, HeartbeatConfig heartbeat_config, Long server_timestamp) { + this.id = id; + this.jid = jid; + this.error = error; + this.setting = immutableCopyOf(setting); + this.stream_id = stream_id; + this.last_stream_id_received = last_stream_id_received; + this.heartbeat_config = heartbeat_config; + this.server_timestamp = server_timestamp; + } + + private LoginResponse(Builder builder) { + this(builder.id, builder.jid, builder.error, builder.setting, builder.stream_id, builder.last_stream_id_received, builder.heartbeat_config, builder.server_timestamp); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof LoginResponse)) return false; + LoginResponse o = (LoginResponse) other; + return equals(id, o.id) + && equals(jid, o.jid) + && equals(error, o.error) + && equals(setting, o.setting) + && equals(stream_id, o.stream_id) + && equals(last_stream_id_received, o.last_stream_id_received) + && equals(heartbeat_config, o.heartbeat_config) + && equals(server_timestamp, o.server_timestamp); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = id != null ? id.hashCode() : 0; + result = result * 37 + (jid != null ? jid.hashCode() : 0); + result = result * 37 + (error != null ? error.hashCode() : 0); + result = result * 37 + (setting != null ? setting.hashCode() : 1); + result = result * 37 + (stream_id != null ? stream_id.hashCode() : 0); + result = result * 37 + (last_stream_id_received != null ? last_stream_id_received.hashCode() : 0); + result = result * 37 + (heartbeat_config != null ? heartbeat_config.hashCode() : 0); + result = result * 37 + (server_timestamp != null ? server_timestamp.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String id; + public String jid; + public ErrorInfo error; + public List setting; + public Integer stream_id; + public Integer last_stream_id_received; + public HeartbeatConfig heartbeat_config; + public Long server_timestamp; + + public Builder() { + } + + public Builder(LoginResponse message) { + super(message); + if (message == null) return; + this.id = message.id; + this.jid = message.jid; + this.error = message.error; + this.setting = copyOf(message.setting); + this.stream_id = message.stream_id; + this.last_stream_id_received = message.last_stream_id_received; + this.heartbeat_config = message.heartbeat_config; + this.server_timestamp = message.server_timestamp; + } + + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Not used. + */ + public Builder jid(String jid) { + this.jid = jid; + return this; + } + + /** + * Null if login was ok. + */ + public Builder error(ErrorInfo error) { + this.error = error; + return this; + } + + public Builder setting(List setting) { + this.setting = checkForNulls(setting); + return this; + } + + public Builder stream_id(Integer stream_id) { + this.stream_id = stream_id; + return this; + } + + /** + * Should be "1" + */ + public Builder last_stream_id_received(Integer last_stream_id_received) { + this.last_stream_id_received = last_stream_id_received; + return this; + } + + public Builder heartbeat_config(HeartbeatConfig heartbeat_config) { + this.heartbeat_config = heartbeat_config; + return this; + } + + /** + * used by the client to synchronize with the server timestamp. + */ + public Builder server_timestamp(Long server_timestamp) { + this.server_timestamp = server_timestamp; + return this; + } + + @Override + public LoginResponse build() { + checkRequiredFields(); + return new LoginResponse(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/SelectiveAck.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/SelectiveAck.java new file mode 100644 index 00000000..dad958be --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/SelectiveAck.java @@ -0,0 +1,68 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; +import java.util.Collections; +import java.util.List; + +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REPEATED; + +/** + * Included in IQ sent after LoginResponse from server with ID 12. + */ +public final class SelectiveAck extends Message { + + public static final List DEFAULT_ID = Collections.emptyList(); + + @ProtoField(tag = 1, type = STRING, label = REPEATED) + public final List id; + + public SelectiveAck(List id) { + this.id = immutableCopyOf(id); + } + + private SelectiveAck(Builder builder) { + this(builder.id); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof SelectiveAck)) return false; + return equals(id, ((SelectiveAck) other).id); + } + + @Override + public int hashCode() { + int result = hashCode; + return result != 0 ? result : (hashCode = id != null ? id.hashCode() : 1); + } + + public static final class Builder extends Message.Builder { + + public List id; + + public Builder() { + } + + public Builder(SelectiveAck message) { + super(message); + if (message == null) return; + this.id = copyOf(message.id); + } + + public Builder id(List id) { + this.id = checkForNulls(id); + return this; + } + + @Override + public SelectiveAck build() { + return new SelectiveAck(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Setting.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Setting.java new file mode 100644 index 00000000..00c9eacc --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/Setting.java @@ -0,0 +1,90 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +/** + * MobileSettings class. + * "u:f", "u:b", "u:s" - multi user devices reporting foreground, background + * and stopped users. + * hbping: heatbeat ping interval + * rmq2v: include explicit stream IDs + */ +public final class Setting extends Message { + + public static final String DEFAULT_NAME = ""; + public static final String DEFAULT_VALUE = ""; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String name; + + @ProtoField(tag = 2, type = STRING, label = REQUIRED) + public final String value; + + public Setting(String name, String value) { + this.name = name; + this.value = value; + } + + private Setting(Builder builder) { + this(builder.name, builder.value); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof Setting)) return false; + Setting o = (Setting) other; + return equals(name, o.name) + && equals(value, o.value); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = name != null ? name.hashCode() : 0; + result = result * 37 + (value != null ? value.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String name; + public String value; + + public Builder() { + } + + public Builder(Setting message) { + super(message); + if (message == null) return; + this.name = message.name; + this.value = message.value; + } + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder value(String value) { + this.value = value; + return this; + } + + @Override + public Setting build() { + checkRequiredFields(); + return new Setting(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamAck.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamAck.java new file mode 100644 index 00000000..d1b65f0b --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamAck.java @@ -0,0 +1,44 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; + +/** + * Included in IQ with ID 13, sent from client or server after 10 unconfirmed + * messages. + */ +public final class StreamAck extends Message { + + public StreamAck() { + } + + private StreamAck(Builder builder) { + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + return other instanceof StreamAck; + } + + @Override + public int hashCode() { + return 0; + } + + public static final class Builder extends Message.Builder { + + public Builder() { + } + + public Builder(StreamAck message) { + super(message); + } + + @Override + public StreamAck build() { + return new StreamAck(this); + } + } +} diff --git a/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamErrorStanza.java b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamErrorStanza.java new file mode 100644 index 00000000..0a836d7a --- /dev/null +++ b/play-services-core/src/main/protos-java/org/microg/gms/gcm/mcs/StreamErrorStanza.java @@ -0,0 +1,83 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source file: protos-repo/mcs.proto +package org.microg.gms.gcm.mcs; + +import com.squareup.wire.Message; +import com.squareup.wire.ProtoField; + +import static com.squareup.wire.Message.Datatype.STRING; +import static com.squareup.wire.Message.Label.REQUIRED; + +public final class StreamErrorStanza extends Message { + + public static final String DEFAULT_TYPE = ""; + public static final String DEFAULT_TEXT = ""; + + @ProtoField(tag = 1, type = STRING, label = REQUIRED) + public final String type; + + @ProtoField(tag = 2, type = STRING) + public final String text; + + public StreamErrorStanza(String type, String text) { + this.type = type; + this.text = text; + } + + private StreamErrorStanza(Builder builder) { + this(builder.type, builder.text); + setBuilder(builder); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (!(other instanceof StreamErrorStanza)) return false; + StreamErrorStanza o = (StreamErrorStanza) other; + return equals(type, o.type) + && equals(text, o.text); + } + + @Override + public int hashCode() { + int result = hashCode; + if (result == 0) { + result = type != null ? type.hashCode() : 0; + result = result * 37 + (text != null ? text.hashCode() : 0); + hashCode = result; + } + return result; + } + + public static final class Builder extends Message.Builder { + + public String type; + public String text; + + public Builder() { + } + + public Builder(StreamErrorStanza message) { + super(message); + if (message == null) return; + this.type = message.type; + this.text = message.text; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder text(String text) { + this.text = text; + return this; + } + + @Override + public StreamErrorStanza build() { + checkRequiredFields(); + return new StreamErrorStanza(this); + } + } +} diff --git a/play-services-core/src/main/protos-repo/mcs.proto b/play-services-core/src/main/protos-repo/mcs.proto new file mode 100644 index 00000000..ebac9eb2 --- /dev/null +++ b/play-services-core/src/main/protos-repo/mcs.proto @@ -0,0 +1,266 @@ +// Derived from mcs.proto in chromium source code. Original license text below. + +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// MCS protocol for communication between Chrome client and Mobile Connection +// Server . + +option java_package = "org.microg.gms.gcm.mcs"; + +/* + Common fields/comments: + + stream_id: no longer sent by server, each side keeps a counter + last_stream_id_received: sent only if a packet was received since last time + a last_stream was sent + status: new bitmask including the 'idle' as bit 0. + + */ + +/** + TAG: 0 + */ +message HeartbeatPing { + optional int32 stream_id = 1; + optional int32 last_stream_id_received = 2; + optional int64 status = 3; +} + +/** + TAG: 1 + */ +message HeartbeatAck { + optional int32 stream_id = 1; + optional int32 last_stream_id_received = 2; + optional int64 status = 3; +} + +message ErrorInfo { + required int32 code = 1; + optional string message = 2; + optional string type = 3; + optional Extension extension = 4; +} + +// MobileSettings class. +// "u:f", "u:b", "u:s" - multi user devices reporting foreground, background +// and stopped users. +// hbping: heatbeat ping interval +// rmq2v: include explicit stream IDs + +message Setting { + required string name = 1; + required string value = 2; +} + +message HeartbeatStat { + required string ip = 1; + required bool timeout = 2; + required int32 interval_ms = 3; +} + +message HeartbeatConfig { + optional bool upload_stat = 1; + optional string ip = 2; + optional int32 interval_ms = 3; +} + +/** + TAG: 2 + */ +message LoginRequest { + enum AuthService { + ANDROID_ID = 2; + } + required string id = 1; // Must be present ( proto required ), may be empty + // string. + // mcs.android.com. + required string domain = 2; + // Decimal android ID + required string user = 3; + + required string resource = 4; + + // Secret + required string auth_token = 5; + + // Format is: android-HEX_DEVICE_ID + // The user is the decimal value. + optional string device_id = 6; + + // RMQ1 - no longer used + optional int64 last_rmq_id = 7; + + repeated Setting setting = 8; + //optional int32 compress = 9; + repeated string received_persistent_id = 10; + + // Replaced by "rmq2v" setting + // optional bool include_stream_ids = 11; + + optional bool adaptive_heartbeat = 12; + optional HeartbeatStat heartbeat_stat = 13; + // Must be true. + optional bool use_rmq2 = 14; + optional int64 account_id = 15; + + // ANDROID_ID = 2 + optional AuthService auth_service = 16; + + optional int32 network_type = 17; + optional int64 status = 18; +} + +/** + * TAG: 3 + */ +message LoginResponse { + required string id = 1; + // Not used. + optional string jid = 2; + // Null if login was ok. + optional ErrorInfo error = 3; + repeated Setting setting = 4; + optional int32 stream_id = 5; + // Should be "1" + optional int32 last_stream_id_received = 6; + optional HeartbeatConfig heartbeat_config = 7; + // used by the client to synchronize with the server timestamp. + optional int64 server_timestamp = 8; +} + +message StreamErrorStanza { + required string type = 1; + optional string text = 2; +} + +/** + * TAG: 4 + */ +message Close { +} + +message Extension { + // 12: SelectiveAck + // 13: StreamAck + required int32 id = 1; + required bytes data = 2; +} + +/** + * TAG: 7 + * IqRequest must contain a single extension. IqResponse may contain 0 or 1 + * extensions. + */ +message IqStanza { + enum IqType { + GET = 0; + SET = 1; + RESULT = 2; + IQ_ERROR = 3; + } + + optional int64 rmq_id = 1; + required IqType type = 2; + required string id = 3; + optional string from = 4; + optional string to = 5; + optional ErrorInfo error = 6; + + // Only field used in the 38+ protocol (besides common last_stream_id_received, status, rmq_id) + optional Extension extension = 7; + + optional string persistent_id = 8; + optional int32 stream_id = 9; + optional int32 last_stream_id_received = 10; + optional int64 account_id = 11; + optional int64 status = 12; +} + +message AppData { + required string key = 1; + required string value = 2; +} + +/** + * TAG: 8 + */ +message DataMessageStanza { + // Not used. + // optional int64 rmq_id = 1; + + // This is the message ID, set by client, DMP.9 (message_id) + optional string id = 2; + + // Project ID of the sender, DMP.1 + required string from = 3; + + // Part of DMRequest - also the key in DataMessageProto. + optional string to = 4; + + // Package name. DMP.2 + required string category = 5; + + // The collapsed key, DMP.3 + optional string token = 6; + + // User data + GOOGLE. prefixed special entries, DMP.4 + repeated AppData app_data = 7; + + // Not used. + optional bool from_trusted_server = 8; + + // Part of the ACK protocol, returned in DataMessageResponse on server side. + // It's part of the key of DMP. + optional string persistent_id = 9; + + // In-stream ack. Increments on each message sent - a bit redundant + // Not used in DMP/DMR. + optional int32 stream_id = 10; + optional int32 last_stream_id_received = 11; + + // Not used. + // optional string permission = 12; + + // Sent by the device shortly after registration. + optional string reg_id = 13; + + // Not used. + // optional string pkg_signature = 14; + // Not used. + // optional string client_id = 15; + + // serial number of the target user, DMP.8 + // It is the 'serial number' according to user manager. + optional int64 device_user_id = 16; + + // Time to live, in seconds. + optional int32 ttl = 17; + // Timestamp ( according to client ) when message was sent by app, in seconds + optional int64 sent = 18; + + // How long has the message been queued before the flush, in seconds. + // This is needed to account for the time difference between server and + // client: server should adjust 'sent' based on his 'receive' time. + optional int32 queued = 19; + + optional int64 status = 20; +} + +/** + Included in IQ with ID 13, sent from client or server after 10 unconfirmed + messages. + */ +message StreamAck { + // No last_streamid_received required. This is included within an IqStanza, + // which includes the last_stream_id_received. +} + +/** + Included in IQ sent after LoginResponse from server with ID 12. +*/ +message SelectiveAck { + repeated string id = 1; +}