From bc532d132c2257e1e01bd89c3390516e1e2b3092 Mon Sep 17 00:00:00 2001 From: mar-v-in Date: Sun, 22 Feb 2015 17:13:35 +0100 Subject: [PATCH] Add GCM register API client Add generic HttpFormClient --- src/org/microg/gms/common/HttpFormClient.java | 152 ++++++++++++++++++ src/org/microg/gms/gcm/Constants.java | 22 +++ src/org/microg/gms/gcm/RegisterRequest.java | 95 +++++++++++ src/org/microg/gms/gcm/RegisterResponse.java | 24 +++ 4 files changed, 293 insertions(+) create mode 100644 src/org/microg/gms/common/HttpFormClient.java create mode 100644 src/org/microg/gms/gcm/Constants.java create mode 100644 src/org/microg/gms/gcm/RegisterRequest.java create mode 100644 src/org/microg/gms/gcm/RegisterResponse.java diff --git a/src/org/microg/gms/common/HttpFormClient.java b/src/org/microg/gms/common/HttpFormClient.java new file mode 100644 index 00000000..58e57f77 --- /dev/null +++ b/src/org/microg/gms/common/HttpFormClient.java @@ -0,0 +1,152 @@ +/* + * 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.common; + +import android.net.Uri; +import android.util.Log; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpFormClient { + private static final String TAG = "GmsHttpFormClient"; + + public static T request(String url, Request request, Class tClass) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("POST"); + connection.setDoInput(true); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + StringBuilder content = new StringBuilder(); + request.prepare(); + for (Field field : request.getClass().getDeclaredFields()) { + try { + field.setAccessible(true); + String value = String.valueOf(field.get(request)); + if (field.isAnnotationPresent(RequestHeader.class)) { + for (String key : field.getAnnotation(RequestHeader.class).value()) { + connection.setRequestProperty(key, value); + } + } else if (field.isAnnotationPresent(RequestContent.class)) { + for (String key : field.getAnnotation(RequestHeader.class).value()) { + if (content.length() > 0) + content.append("&"); + content.append(Uri.encode(key)).append("=").append(Uri.encode(value)); + } + } + } catch (Exception ignored) { + } + } + + Log.d(TAG, "-- Request --\n" + content); + OutputStream os = connection.getOutputStream(); + os.write(content.toString().getBytes()); + os.close(); + + if (connection.getResponseCode() != 200) { + throw new IOException(connection.getResponseMessage()); + } + + String result = new String(Utils.readStreamToEnd(connection.getInputStream())); + Log.d(TAG, "-- Response --\n" + result); + return parseResponse(tClass, result); + } + + private static T parseResponse(Class tClass, String result) { + T response; + try { + response = tClass.getConstructor().newInstance(); + } catch (Exception e) { + return null; + } + String[] entries = result.split("\n"); + for (String s : entries) { + String[] keyValuePair = s.split("=", 2); + String key = keyValuePair[0].trim(); + String value = keyValuePair[1].trim(); + try { + for (Field field : tClass.getDeclaredFields()) { + if (field.isAnnotationPresent(ResponseField.class) && + key.equals(field.getAnnotation(ResponseField.class).value())) { + if (field.getType().equals(String.class)) { + field.set(response, value); + } else if (field.getType().equals(boolean.class)) { + field.setBoolean(response, value.equals("1")); + } else if (field.getType().equals(long.class)) { + field.setLong(response, Long.parseLong(value)); + } else if (field.getType().equals(int.class)) { + field.setInt(response, Integer.parseInt(value)); + } + } + } + } catch (Exception e) { + Log.w(TAG, e); + } + } + return response; + } + + public static void requestAsync(final String url, final Request request, final Class tClass, + final Callback callback) { + new Thread(new Runnable() { + @Override + public void run() { + try { + callback.onResponse(request(url, request, tClass)); + } catch (Exception e) { + callback.onException(e); + } + } + }).start(); + } + + public static abstract class Request { + protected void prepare() { + } + } + + public interface Callback { + void onResponse(T response); + + void onException(Exception exception); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RequestHeader { + public String[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface RequestContent { + public String[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface ResponseField { + public String value(); + } +} diff --git a/src/org/microg/gms/gcm/Constants.java b/src/org/microg/gms/gcm/Constants.java new file mode 100644 index 00000000..03e8670a --- /dev/null +++ b/src/org/microg/gms/gcm/Constants.java @@ -0,0 +1,22 @@ +/* + * 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; + +public class Constants { + public static final String REGISTER_URL = "https://android.clients.google.com/c2dm/register3"; + +} diff --git a/src/org/microg/gms/gcm/RegisterRequest.java b/src/org/microg/gms/gcm/RegisterRequest.java new file mode 100644 index 00000000..4b6261cf --- /dev/null +++ b/src/org/microg/gms/gcm/RegisterRequest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013-2015 µg Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.gcm; + +import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.Build; +import org.microg.gms.common.HttpFormClient; + +import java.io.IOException; + +import static org.microg.gms.common.HttpFormClient.RequestContent; +import static org.microg.gms.common.HttpFormClient.RequestHeader; + +public class RegisterRequest extends HttpFormClient.Request { + private static final String USER_AGENT = "Android-GCM/1.3 (%s %s)"; + + @RequestHeader("Authorization") + private String auth; + @RequestHeader("User-Agent") + private String userAgent; + + @RequestHeader("app") + @RequestContent("app") + public String app; + @RequestContent("cert") + public String appSignature; + @RequestContent("app_ver") + public int appVersion; + @RequestContent("info") + public String info; + @RequestContent("sender") + public String sender; + @RequestContent({"X-GOOG.USER_AID", "device"}) + public long androidId; + public long securityToken; + public String deviceName; + public String buildVersion; + + @Override + public void prepare() { + userAgent = String.format(USER_AGENT, deviceName, buildVersion); + auth = "AidLogin " + androidId + ":" + securityToken; + } + + public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) { + androidId = lastCheckinInfo.androidId; + securityToken = lastCheckinInfo.securityToken; + return this; + } + + public RegisterRequest app(String app, String appSignature, int appVersion) { + this.app = app; + this.appSignature = appSignature; + this.appVersion = appVersion; + return this; + } + + public RegisterRequest info(String info) { + this.info = info; + return this; + } + + public RegisterRequest sender(String sender) { + this.sender = sender; + return this; + } + + public RegisterRequest build(Build build) { + deviceName = build.device; + buildVersion = build.id; + return this; + } + + public RegisterResponse getResponse() throws IOException { + return HttpFormClient.request(Constants.REGISTER_URL, this, RegisterResponse.class); + } + + public void getResponseAsync(HttpFormClient.Callback callback) { + HttpFormClient.requestAsync(Constants.REGISTER_URL, this, RegisterResponse.class, callback); + } +} diff --git a/src/org/microg/gms/gcm/RegisterResponse.java b/src/org/microg/gms/gcm/RegisterResponse.java new file mode 100644 index 00000000..3ee4b8fb --- /dev/null +++ b/src/org/microg/gms/gcm/RegisterResponse.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013-2015 µg Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.gcm; + +import static org.microg.gms.common.HttpFormClient.ResponseField; + +public class RegisterResponse { + @ResponseField("token") + public String token; +}