Fix FCM registration for apps using firebase >= 20.1.1

With version 20.1.1 the Firebase Cloud Messaging SDK started to use
the Firebase Installations SDK, which affects the FCM registration
process.

The implementation of FCM registration in microG failed to pass extra
parameters that became relevant with the introduction of the
Firebase Installations SDK to the FCM registration endpoint.

These additional parameters are passed through to the endpoint with an 'X-' prefix.
This commit is contained in:
georgeto 2020-06-09 02:52:03 +02:00 committed by Marvin W
parent 8eff51cfb6
commit 5146559f89
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
6 changed files with 79 additions and 37 deletions

View File

@ -46,6 +46,13 @@ public class HttpFormClient {
try {
field.setAccessible(true);
Object objVal = field.get(request);
if (field.isAnnotationPresent(RequestContentDynamic.class)) {
Map<String, String> contentParams = (Map<String, String>) objVal;
for (Map.Entry<String, String> param : contentParams.entrySet()) {
appendParam(content, param.getKey(), param.getValue());
}
continue;
}
String value = objVal != null ? String.valueOf(objVal) : null;
Boolean boolVal = null;
if (field.getType().equals(boolean.class)) {
@ -65,9 +72,7 @@ public class HttpFormClient {
value = valueFromBoolVal(value, boolVal, annotation.truePresent(), annotation.falsePresent());
if (value != null || annotation.nullPresent()) {
for (String key : annotation.value()) {
if (content.length() > 0)
content.append("&");
content.append(Uri.encode(key)).append("=").append(Uri.encode(String.valueOf(value)));
appendParam(content, key, value);
}
}
}
@ -109,6 +114,12 @@ public class HttpFormClient {
}
}
private static void appendParam(StringBuilder content, String key, String value) {
if (content.length() > 0)
content.append("&");
content.append(Uri.encode(key)).append("=").append(Uri.encode(String.valueOf(value)));
}
private static <T> T parseResponse(Class<T> tClass, HttpURLConnection connection, String result) throws IOException {
Map<String, List<String>> headerFields = connection.getHeaderFields();
T response;
@ -227,6 +238,11 @@ public class HttpFormClient {
public boolean nullPresent() default false;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RequestContentDynamic {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ResponseField {

View File

@ -264,4 +264,12 @@ public class PackageUtils {
return null;
}
}
public static int targetSdkVersion(Context context, String packageName) {
try {
return context.getPackageManager().getApplicationInfo(packageName, 0).targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
}

View File

@ -145,7 +145,7 @@ class PushRegisterHandler extends Handler {
.checkin(LastCheckinInfo.read(context))
.app(packageName)
.delete(delete)
.appid(subdata.getString("appid"), subdata.getString("gmp_app_id")),
.extraParams(subdata),
bundle -> sendReply(what, id, replyTo, bundle));
}
}

View File

@ -70,12 +70,16 @@ public class PushRegisterManager {
public static void completeRegisterRequest(Context context, GcmDatabase database, String requestId, RegisterRequest request, BundleCallback callback) {
if (request.app != null) {
if (request.appSignature == null)
request.appSignature = PackageUtils.firstSignatureDigest(context, request.app);
if (request.appVersion <= 0)
request.appVersion = PackageUtils.versionCode(context, request.app);
if (request.appVersionName == null)
request.appVersionName = PackageUtils.versionName(context, request.app);
if (!request.delete) {
if (request.appSignature == null) {
request.appSignature = PackageUtils.firstSignatureDigest(context, request.app);
}
request.sdkVersion = PackageUtils.targetSdkVersion(context, request.app);
if (!request.hasExtraParam(GcmConstants.EXTRA_APP_VERSION_NAME))
request.extraParam(GcmConstants.EXTRA_APP_VERSION_NAME, PackageUtils.versionName(context, request.app));
}
}
GcmDatabase.App app = database.getApp(request.app);

View File

@ -20,7 +20,6 @@ import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.Message;
@ -140,7 +139,8 @@ public class PushRegisterService extends IntentService {
.build(Utils.getBuild(context))
.sender(intent.getStringExtra(EXTRA_SENDER))
.checkin(LastCheckinInfo.read(context))
.app(packageName),
.app(packageName)
.extraParams(intent.getExtras()),
bundle -> {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
outIntent.putExtras(bundle);
@ -176,7 +176,8 @@ public class PushRegisterService extends IntentService {
.build(Utils.getBuild(this))
.sender(intent.getStringExtra(EXTRA_SENDER))
.checkin(LastCheckinInfo.read(this))
.app(packageName),
.app(packageName)
.extraParams(intent.getExtras()),
bundle -> {
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
outIntent.putExtras(bundle);

View File

@ -16,19 +16,24 @@
package org.microg.gms.gcm;
import android.os.Bundle;
import android.text.TextUtils;
import org.microg.gms.checkin.LastCheckinInfo;
import org.microg.gms.common.Build;
import org.microg.gms.common.Constants;
import org.microg.gms.common.HttpFormClient;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.microg.gms.common.HttpFormClient.RequestContent;
import static org.microg.gms.common.HttpFormClient.RequestContentDynamic;
import static org.microg.gms.common.HttpFormClient.RequestHeader;
public class RegisterRequest extends HttpFormClient.Request {
private static final String SERVICE_URL = "https://android.clients.google.com/c2dm/register3";
private static final String USER_AGENT = "Android-GCM/1.3 (%s %s)";
private static final String USER_AGENT = "Android-GCM/1.5 (%s %s)";
@RequestHeader("Authorization")
private String auth;
@ -42,35 +47,26 @@ public class RegisterRequest extends HttpFormClient.Request {
public String appSignature;
@RequestContent("app_ver")
public int appVersion;
@RequestContent("app_ver_name")
public String appVersionName;
@RequestContent("info")
public String info;
@RequestContent({"sender", "subtype"})
@RequestContent("sender")
public String sender;
@RequestContent({"X-GOOG.USER_AID", "device"})
@RequestContent("device")
public long androidId;
@RequestContent("delete")
public boolean delete;
public long securityToken;
public String deviceName;
public String buildVersion;
@RequestContent("osv")
public int sdkVersion;
@RequestContent("gmsv")
public int gmsVersion;
@RequestContent("scope")
public String scope = "*";
@RequestContent("appid")
public String appId;
@RequestContent("gmp_app_id")
public String gmpAppId;
@RequestContent("target_ver")
public Integer sdkVersion;
@RequestContentDynamic
private Map<String, String> extraParams = new LinkedHashMap<>();
@Override
public void prepare() {
userAgent = String.format(USER_AGENT, deviceName, buildVersion);
auth = "AidLogin " + androidId + ":" + securityToken;
gmsVersion = Constants.MAX_REFERENCE_VERSION;
}
public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) {
@ -90,17 +86,10 @@ public class RegisterRequest extends HttpFormClient.Request {
return this;
}
public RegisterRequest app(String app, String appSignature, int appVersion, String appVersionName) {
public RegisterRequest app(String app, String appSignature, int appVersion) {
this.app = app;
this.appSignature = appSignature;
this.appVersion = appVersion;
this.appVersionName = appVersionName;
return this;
}
public RegisterRequest appid(String appid, String gmpAppId) {
this.appId = appid;
this.gmpAppId = gmpAppId;
return this;
}
@ -117,7 +106,6 @@ public class RegisterRequest extends HttpFormClient.Request {
public RegisterRequest build(Build build) {
deviceName = build.device;
buildVersion = build.id;
sdkVersion = build.sdk;
return this;
}
@ -130,6 +118,31 @@ public class RegisterRequest extends HttpFormClient.Request {
return this;
}
public RegisterRequest extraParams(Bundle extraBundle) {
for (String key : extraBundle.keySet()) {
if (!key.equals(GcmConstants.EXTRA_SENDER) && !key.equals(GcmConstants.EXTRA_DELETE)) {
extraParam(key, extraBundle.getString(key));
}
}
return this;
}
public RegisterRequest extraParam(String key, String value) {
// Ignore empty registration extras
if (!TextUtils.isEmpty(value)) {
extraParams.put(extraParamKey(key), value);
}
return this;
}
public boolean hasExtraParam(String key) {
return extraParams.containsKey(extraParamKey(key));
}
private static String extraParamKey(String key) {
return "X-" + key;
}
public RegisterResponse getResponse() throws IOException {
return HttpFormClient.request(SERVICE_URL, this, RegisterResponse.class);
}