From 719cd51d3dc4414d0bbfc00b63892b33649d1393 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 31 Aug 2018 12:39:56 +0200 Subject: [PATCH] Push Messaging: Handle more edge cases, fix some apps not showing up as registered --- .../src/main/AndroidManifest.xml | 10 +- .../org/microg/gms/common/HttpFormClient.java | 8 +- .../org/microg/gms/common/PackageUtils.java | 8 + .../microg/gms/gcm/PushRegisterHandler.java | 151 +++++++++ .../microg/gms/gcm/PushRegisterManager.java | 168 ++++++++++ .../microg/gms/gcm/PushRegisterReceiver.java | 40 +++ .../microg/gms/gcm/PushRegisterService.java | 300 +++--------------- .../org/microg/gms/gcm/RegisterRequest.java | 37 ++- .../microg/gms/gcm/UnregisterReceiver.java | 2 +- .../org/microg/gms/ui/AskPushPermission.java | 2 +- .../org/microg/gms/ui/GcmAppFragment.java | 5 +- 11 files changed, 467 insertions(+), 264 deletions(-) create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterReceiver.java diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 6951a9418f..80b1b7ab8f 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -205,16 +205,18 @@ android:permission="com.google.android.c2dm.permission.RECEIVE"> - - - - + + + + + + T request(String url, Request request, Class tClass) throws os.close(); if (connection.getResponseCode() != 200) { - throw new IOException(connection.getResponseMessage()); + String error = connection.getResponseMessage(); + try { + error = new String(Utils.readStreamToEnd(connection.getErrorStream())); + } catch (IOException e) { + // Ignore + } + throw new IOException(error); } String result = new String(Utils.readStreamToEnd(connection.getInputStream())); diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java index e38d314bd8..ba247f1263 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -155,4 +155,12 @@ public static int versionCode(Context context, String packageName) { return -1; } } + + public static String versionName(Context context, String packageName) { + try { + return context.getPackageManager().getPackageInfo(packageName, 0).versionName; + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java new file mode 100644 index 0000000000..f161533c5b --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2018 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.gcm; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.PackageUtils; +import org.microg.gms.common.Utils; + +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; +import static org.microg.gms.gcm.GcmConstants.ERROR_SERVICE_NOT_AVAILABLE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; + +class PushRegisterHandler extends Handler { + private static final String TAG = "GmsGcmRegisterHdl"; + + private Context context; + private int callingUid; + private GcmDatabase database; + + public PushRegisterHandler(Context context, GcmDatabase database) { + this.context = context; + this.database = database; + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + this.callingUid = Binder.getCallingUid(); + return super.sendMessageAtTime(msg, uptimeMillis); + } + + private void sendReply(int what, int id, Messenger replyTo, Bundle data) { + if (what == 0) { + Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); + outIntent.putExtras(data); + Message message = Message.obtain(); + message.obj = outIntent; + try { + replyTo.send(message); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } else { + Bundle messageData = new Bundle(); + messageData.putBundle("data", data); + Message response = Message.obtain(); + response.what = what; + response.arg1 = id; + response.setData(messageData); + try { + replyTo.send(response); + } catch (RemoteException e) { + Log.w(TAG, e); + } + } + } + + private void replyError(int what, int id, Messenger replyTo, String errorMessage) { + Bundle bundle = new Bundle(); + bundle.putString(EXTRA_ERROR, errorMessage); + sendReply(what, id, replyTo, bundle); + } + + private void replyNotAvailable(int what, int id, Messenger replyTo) { + replyError(what, id, replyTo, ERROR_SERVICE_NOT_AVAILABLE); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == 0) { + if (msg.obj instanceof Intent) { + Message nuMsg = Message.obtain(); + nuMsg.what = msg.what; + nuMsg.arg1 = 0; + nuMsg.replyTo = null; + PendingIntent pendingIntent = ((Intent) msg.obj).getParcelableExtra(EXTRA_APP); + String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); + Bundle data = new Bundle(); + data.putBoolean("oneWay", false); + data.putString("pkg", packageName); + data.putBundle("data", msg.getData()); + nuMsg.setData(data); + msg = nuMsg; + } else { + return; + } + } + + int what = msg.what; + int id = msg.arg1; + Messenger replyTo = msg.replyTo; + if (replyTo == null) { + Log.w(TAG, "replyTo is null"); + return; + } + Bundle data = msg.getData(); + if (data.getBoolean("oneWay", false)) { + Log.w(TAG, "oneWay requested"); + return; + } + + String packageName = data.getString("pkg"); + Bundle subdata = data.getBundle("data"); + String sender = subdata.getString("sender"); + boolean delete = subdata.get("delete") != null; + + try { + PackageUtils.checkPackageUid(context, packageName, callingUid); + } catch (SecurityException e) { + Log.w(TAG, e); + return; + } + + // TODO: We should checkin and/or ask for permission here. + + PushRegisterManager.completeRegisterRequest(context, database, + new RegisterRequest() + .build(Utils.getBuild(context)) + .sender(sender) + .checkin(LastCheckinInfo.read(context)) + .app(packageName) + .delete(delete) + .appid(subdata.getString("appid"), subdata.getString("gmp_app_id")), + bundle -> sendReply(what, id, replyTo, bundle)); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java new file mode 100644 index 0000000000..7ae7d64801 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2018 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.gcm; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.HttpFormClient; +import org.microg.gms.common.PackageUtils; +import org.microg.gms.common.Utils; + +import java.io.IOException; + +import static org.microg.gms.gcm.GcmConstants.ERROR_SERVICE_NOT_AVAILABLE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_RETRY_AFTER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED; + +public class PushRegisterManager { + private static final String TAG = "GmsGcmRegisterMgr"; + + public static RegisterResponse unregister(Context context, String packageName, String pkgSignature, String sender, String info) { + GcmDatabase database = new GcmDatabase(context); + RegisterResponse response = new RegisterResponse(); + try { + response = new RegisterRequest() + .build(Utils.getBuild(context)) + .sender(sender) + .info(info) + .checkin(LastCheckinInfo.read(context)) + .app(packageName, pkgSignature) + .delete(true) + .getResponse(); + } catch (IOException e) { + Log.w(TAG, e); + } + if (!packageName.equals(response.deleted)) { + database.noteAppRegistrationError(packageName, response.responseText); + } else { + database.noteAppUnregistered(packageName, pkgSignature); + } + database.close(); + return response; + } + + public interface BundleCallback { + void onResult(Bundle bundle); + } + + public static void completeRegisterRequest(Context context, GcmDatabase database, RegisterRequest request, BundleCallback callback) { + completeRegisterRequest(context, database, null, request, callback); + } + + 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); + } + + GcmDatabase.App app = database.getApp(request.app); + GcmPrefs prefs = GcmPrefs.get(context); + if (!request.delete) { + if (!prefs.isEnabled() || + (app != null && !app.allowRegister) || + LastCheckinInfo.read(context).lastCheckin <= 0 || + (app == null && prefs.isConfirmNewApps())) { + Bundle bundle = new Bundle(); + bundle.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE); + callback.onResult(bundle); + return; + } + } else { + if (database.getRegistrationsByApp(request.app).isEmpty()) { + Bundle bundle = new Bundle(); + bundle.putString(EXTRA_UNREGISTERED, attachRequestId(request.app, requestId)); + callback.onResult(bundle); + return; + } + } + + request.getResponseAsync(new HttpFormClient.Callback() { + @Override + public void onResponse(RegisterResponse response) { + callback.onResult(handleResponse(database, request, response, requestId)); + } + + @Override + public void onException(Exception e) { + Log.w(TAG, e); + callback.onResult(handleResponse(database, request, e, requestId)); + } + }); + } + + + + private static Bundle handleResponse(GcmDatabase database, RegisterRequest request, RegisterResponse response, String requestId) { + return handleResponse(database, request, response, null, requestId); + } + + private static Bundle handleResponse(GcmDatabase database, RegisterRequest request, Exception e, String requestId) { + return handleResponse(database, request, null, e, requestId); + } + + private static Bundle handleResponse(GcmDatabase database, RegisterRequest request, RegisterResponse response, Exception e, String requestId) { + Bundle resultBundle = new Bundle(); + if (response == null && e == null) { + resultBundle.putString(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); + } else if (e != null) { + if (e.getMessage() != null && e.getMessage().startsWith("Error=")) { + String errorMessage = e.getMessage().substring(6); + database.noteAppRegistrationError(request.app, errorMessage); + resultBundle.putString(EXTRA_ERROR, attachRequestId(errorMessage, requestId)); + } else { + resultBundle.putString(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); + } + } else { + if (!request.delete) { + if (response.token == null) { + database.noteAppRegistrationError(request.app, response.responseText); + resultBundle.putString(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); + } else { + database.noteAppRegistered(request.app, request.appSignature, response.token); + resultBundle.putString(EXTRA_REGISTRATION_ID, attachRequestId(response.token, requestId)); + } + } else { + if (!request.app.equals(response.deleted) && !request.app.equals(response.token)) { + database.noteAppRegistrationError(request.app, response.responseText); + resultBundle.putString(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); + } else { + database.noteAppUnregistered(request.app, request.appSignature); + resultBundle.putString(EXTRA_UNREGISTERED, attachRequestId(request.app, requestId)); + } + } + + if (response.retryAfter != null && !response.retryAfter.contains(":")) { + resultBundle.putLong(EXTRA_RETRY_AFTER, Long.parseLong(response.retryAfter)); + } + } + return resultBundle; + } + + public static String attachRequestId(String msg, String requestId) { + if (requestId == null) return msg; + return "|ID|" + requestId + "|" + msg; + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterReceiver.java new file mode 100644 index 0000000000..ca9c0e5506 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterReceiver.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 microG Project Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.microg.gms.gcm; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.WakefulBroadcastReceiver; + +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER; +import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER; + +public class PushRegisterReceiver extends WakefulBroadcastReceiver { + private static final String TAG = "GmsGcmRegisterRcv"; + + @Override + public void onReceive(Context context, Intent intent) { + Intent intent2 = new Intent(context, PushRegisterService.class); + if (intent.getExtras().get("delete") != null) { + intent2.setAction(ACTION_C2DM_UNREGISTER); + } else { + intent2.setAction(ACTION_C2DM_REGISTER); + } + intent2.putExtras(intent.getExtras()); + startWakefulService(context, intent2); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java index 91ccdb3209..b831f083f9 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterService.java @@ -22,15 +22,11 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.os.AsyncTask; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.os.RemoteException; import android.support.annotation.Nullable; +import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; import org.microg.gms.checkin.CheckinService; @@ -39,8 +35,6 @@ import org.microg.gms.common.Utils; import org.microg.gms.ui.AskPushPermission; -import java.io.IOException; - import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER; @@ -51,10 +45,7 @@ import static org.microg.gms.gcm.GcmConstants.EXTRA_KID; import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER; import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT; -import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; -import static org.microg.gms.gcm.GcmConstants.EXTRA_RETRY_AFTER; import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER; -import static org.microg.gms.gcm.GcmConstants.EXTRA_UNREGISTERED; public class PushRegisterService extends IntentService { private static final String TAG = "GmsGcmRegisterSvc"; @@ -80,33 +71,9 @@ public void onDestroy() { database.close(); } - public static RegisterResponse register(Context context, String packageName, String pkgSignature, String sender, String info) { - GcmDatabase database = new GcmDatabase(context); - RegisterResponse response = register(context, packageName, pkgSignature, sender, info, false); - String regId = response.token; - if (regId != null) { - database.noteAppRegistered(packageName, pkgSignature, regId); - } else { - database.noteAppRegistrationError(packageName, response.responseText); - } - database.close(); - return response; - } - - public static RegisterResponse unregister(Context context, String packageName, String pkgSignature, String sender, String info) { - GcmDatabase database = new GcmDatabase(context); - RegisterResponse response = register(context, packageName, pkgSignature, sender, info, true); - if (!packageName.equals(response.deleted)) { - database.noteAppRegistrationError(packageName, response.responseText); - } else { - database.noteAppUnregistered(packageName, pkgSignature); - } - database.close(); - return response; - } - @Override protected void onHandleIntent(Intent intent) { + WakefulBroadcastReceiver.completeWakefulIntent(intent); Log.d(TAG, "onHandleIntent: " + intent); String requestId = null; @@ -117,42 +84,35 @@ protected void onHandleIntent(Intent intent) { } } - if (GcmPrefs.get(this).isEnabled()) { - if (LastCheckinInfo.read(this).lastCheckin > 0) { - try { - if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) || - (ACTION_C2DM_REGISTER.equals(intent.getAction()) && "1".equals(intent.getStringExtra(EXTRA_DELETE)))) { - unregister(intent, requestId); - } else if (ACTION_C2DM_REGISTER.equals(intent.getAction())) { - register(intent, requestId); - } - } catch (Exception e) { - Log.w(TAG, e); + if (LastCheckinInfo.read(this).lastCheckin > 0) { + try { + if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) || + (ACTION_C2DM_REGISTER.equals(intent.getAction()) && "1".equals(intent.getStringExtra(EXTRA_DELETE)))) { + unregister(intent, requestId); + } else if (ACTION_C2DM_REGISTER.equals(intent.getAction())) { + register(intent, requestId); } - } else if (!intent.getBooleanExtra(EXTRA_SKIP_TRY_CHECKIN, false)) { - Log.d(TAG, "No checkin yet, trying to checkin"); - intent.putExtra(EXTRA_SKIP_TRY_CHECKIN, true); - Intent subIntent = new Intent(this, CheckinService.class); - subIntent.putExtra(CheckinService.EXTRA_FORCE_CHECKIN, true); - subIntent.putExtra(CheckinService.EXTRA_CALLBACK_INTENT, intent); - startService(subIntent); + } catch (Exception e) { + Log.w(TAG, e); } - } else { - // GCM is disabled, deny registration - replyNotAvailable(this, intent, null, requestId); + } else if (!intent.getBooleanExtra(EXTRA_SKIP_TRY_CHECKIN, false)) { + Log.d(TAG, "No checkin yet, trying to checkin"); + intent.putExtra(EXTRA_SKIP_TRY_CHECKIN, true); + Intent subIntent = new Intent(this, CheckinService.class); + subIntent.putExtra(CheckinService.EXTRA_FORCE_CHECKIN, true); + subIntent.putExtra(CheckinService.EXTRA_CALLBACK_INTENT, intent); + startService(subIntent); } } private void register(final Intent intent, String requestId) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); final String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); - Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras()); GcmDatabase.App app = database.getApp(packageName); if (app == null && GcmPrefs.get(this).isConfirmNewApps()) { try { - PackageManager pm = getPackageManager(); - ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + getPackageManager().getApplicationInfo(packageName, 0); // Check package exists Intent i = new Intent(this, AskPushPermission.class); i.putExtra(EXTRA_PENDING_INTENT, intent); i.putExtra(EXTRA_APP, packageName); @@ -161,46 +121,31 @@ private void register(final Intent intent, String requestId) { } catch (PackageManager.NameNotFoundException e) { replyNotAvailable(this, intent, packageName, requestId); } - } else if (app != null && !app.allowRegister) { - replyNotAvailable(this, intent, packageName, requestId); } else { - registerAndReply(this, intent, packageName, requestId); + registerAndReply(this, database, intent, packageName, requestId); } } public static void replyNotAvailable(Context context, Intent intent, String packageName, String requestId) { - if (packageName == null) { - PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); - packageName = PackageUtils.packageFromPendingIntent(pendingIntent); - } - if (packageName == null) { - // skip reply - return; - } Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); - outIntent.putExtra(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); - Log.d(TAG, "registration not allowed"); + outIntent.putExtra(EXTRA_ERROR, PushRegisterManager.attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); sendReply(context, intent, packageName, outIntent); } - public static void registerAndReply(Context context, Intent intent, String packageName, String requestId) { - String sender = intent.getStringExtra(EXTRA_SENDER); - String appSignature = PackageUtils.firstSignatureDigest(context, packageName); - String regId = register(context, packageName, appSignature, sender, null).token; - Intent outIntent = createRegistrationReply(regId, requestId); - - Log.d(TAG, "register[res]: " + outIntent + " extras=" + outIntent.getExtras()); - sendReply(context, intent, packageName, outIntent); - } - - private static Intent createRegistrationReply(String regId, String requestId) { - Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); - if (regId != null) { - outIntent.putExtra(EXTRA_REGISTRATION_ID, attachRequestId(regId, requestId)); - } else { - outIntent.putExtra(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); - } - return outIntent; + public static void registerAndReply(Context context, GcmDatabase database, Intent intent, String packageName, String requestId) { + Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras()); + PushRegisterManager.completeRegisterRequest(context, database, + new RegisterRequest() + .build(Utils.getBuild(context)) + .sender(intent.getStringExtra(EXTRA_SENDER)) + .checkin(LastCheckinInfo.read(context)) + .app(packageName), + bundle -> { + Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); + outIntent.putExtras(bundle); + Log.d(TAG, "register[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras()); + sendReply(context, intent, packageName, outIntent); + }); } private static void sendReply(Context context, Intent intent, String packageName, Intent outIntent) { @@ -220,55 +165,23 @@ private static void sendReply(Context context, Intent intent, String packageName context.sendOrderedBroadcast(outIntent, null); } - public static RegisterResponse register(Context context, String app, String appSignature, String sender, String info, boolean delete) { - try { - RegisterResponse response = new RegisterRequest() - .build(Utils.getBuild(context)) - .sender(sender) - .info(info) - .checkin(LastCheckinInfo.read(context)) - .app(app, appSignature, PackageUtils.versionCode(context, app)) - .delete(delete) - .getResponse(); - Log.d(TAG, "received response: " + response); - return response; - } catch (IOException e) { - Log.w(TAG, e); - } - - return new RegisterResponse(); - } - private void unregister(Intent intent, String requestId) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.getExtras()); - Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); - String appSignature = PackageUtils.firstSignatureDigest(this, packageName); - - if (database.getRegistration(packageName, appSignature) == null) { - outIntent.putExtra(EXTRA_UNREGISTERED, attachRequestId(packageName, requestId)); - } else { - RegisterResponse response = unregister(this, packageName, appSignature, null, null); - if (!packageName.equals(response.deleted)) { - outIntent.putExtra(EXTRA_ERROR, attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId)); - - if (response.retryAfter != null && !response.retryAfter.contains(":")) { - outIntent.putExtra(EXTRA_RETRY_AFTER, Long.parseLong(response.retryAfter)); - } - } else { - outIntent.putExtra(EXTRA_UNREGISTERED, attachRequestId(packageName, requestId)); - } - } - - Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras()); - sendReply(this, intent, packageName, outIntent); - } - - private static String attachRequestId(String msg, String requestId) { - if (requestId == null) return msg; - return "|ID|" + requestId + "|" + msg; + PushRegisterManager.completeRegisterRequest(this, database, + new RegisterRequest() + .build(Utils.getBuild(this)) + .sender(intent.getStringExtra(EXTRA_SENDER)) + .checkin(LastCheckinInfo.read(this)) + .app(packageName), + bundle -> { + Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); + outIntent.putExtras(bundle); + Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras()); + sendReply(this, intent, packageName, outIntent); + }); } @Nullable @@ -276,127 +189,10 @@ private static String attachRequestId(String msg, String requestId) { public IBinder onBind(Intent intent) { Log.d(TAG, "onBind: " + intent.toString()); if (ACTION_C2DM_REGISTER.equals(intent.getAction())) { - Messenger messenger = new Messenger(new FcmHandler(this)); + Messenger messenger = new Messenger(new PushRegisterHandler(this, database)); return messenger.getBinder(); } return super.onBind(intent); } - private static class FcmRegisterTask extends AsyncTask { - private Context context; - private String packageName; - private String sender; - private Callback callback; - - public FcmRegisterTask(Context context, String packageName, String sender, Callback callback) { - this.context = context; - this.packageName = packageName; - this.sender = sender; - this.callback = callback; - } - - public interface Callback { - void onResult(RegisterResponse registerResponse); - } - - @Override - protected RegisterResponse doInBackground(Void... voids) { - return register(context, packageName, PackageUtils.firstSignatureDigest(context, packageName), sender, null, false); - } - - @Override - protected void onPostExecute(RegisterResponse registerResponse) { - callback.onResult(registerResponse); - } - } - - private static class FcmHandler extends Handler { - private Context context; - private int callingUid; - - public FcmHandler(Context context) { - this.context = context; - } - - @Override - public boolean sendMessageAtTime(Message msg, long uptimeMillis) { - this.callingUid = Binder.getCallingUid(); - return super.sendMessageAtTime(msg, uptimeMillis); - } - - @Override - public void handleMessage(Message msg) { - if (msg.what == 0) { - if (msg.obj instanceof Intent) { - Message nuMsg = Message.obtain(); - nuMsg.what = msg.what; - nuMsg.arg1 = 0; - nuMsg.replyTo = null; - PendingIntent pendingIntent = ((Intent) msg.obj).getParcelableExtra(EXTRA_APP); - String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); - Bundle data = new Bundle(); - data.putBoolean("oneWay", false); - data.putString("pkg", packageName); - data.putBundle("data", msg.getData()); - nuMsg.setData(data); - msg = nuMsg; - } else { - return; - } - } - int what = msg.what; - int id = msg.arg1; - Messenger replyTo = msg.replyTo; - if (replyTo == null) { - Log.w(TAG, "replyTo is null"); - return; - } - Bundle data = msg.getData(); - if (data.getBoolean("oneWay", false)) { - Log.w(TAG, "oneWay requested"); - return; - } - String packageName = data.getString("pkg"); - Bundle subdata = data.getBundle("data"); - String sender = subdata.getString("sender"); - try { - PackageUtils.checkPackageUid(context, packageName, callingUid); - } catch (SecurityException e) { - Log.w(TAG, e); - return; - } - new FcmRegisterTask(context, packageName, sender, registerResponse -> { - Bundle data1 = new Bundle(); - if (registerResponse != null) { - data1.putString(EXTRA_REGISTRATION_ID, registerResponse.token); - } else { - data1.putString(EXTRA_ERROR, ERROR_SERVICE_NOT_AVAILABLE); - } - - if (what == 0) { - Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); - outIntent.putExtras(data1); - Message message = Message.obtain(); - message.obj = outIntent; - try { - replyTo.send(message); - } catch (RemoteException e) { - Log.w(TAG, e); - } - } else { - Bundle messageData = new Bundle(); - messageData.putBundle("data", data1); - Message response = Message.obtain(); - response.what = what; - response.arg1 = id; - response.setData(messageData); - try { - replyTo.send(response); - } catch (RemoteException e) { - Log.w(TAG, e); - } - } - }).execute(); - } - } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java index 183dc0fbe0..6e0d78716f 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/RegisterRequest.java @@ -18,6 +18,7 @@ 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; @@ -41,9 +42,11 @@ 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") + @RequestContent({"sender", "subtype"}) public String sender; @RequestContent({"X-GOOG.USER_AID", "device"}) public long androidId; @@ -52,11 +55,22 @@ public class RegisterRequest extends HttpFormClient.Request { 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; @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) { @@ -65,10 +79,28 @@ public RegisterRequest checkin(LastCheckinInfo lastCheckinInfo) { return this; } - public RegisterRequest app(String app, String appSignature, int appVersion) { + public RegisterRequest app(String app) { + this.app = app; + return this; + } + + public RegisterRequest app(String app, String appSignature) { + this.app = app; + this.appSignature = appSignature; + return this; + } + + public RegisterRequest app(String app, String appSignature, int appVersion, String appVersionName) { 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; } @@ -85,6 +117,7 @@ public RegisterRequest sender(String sender) { public RegisterRequest build(Build build) { deviceName = build.device; buildVersion = build.id; + sdkVersion = build.sdk; return this; } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/UnregisterReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/UnregisterReceiver.java index 5496ce026b..239c55c9bb 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/UnregisterReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/UnregisterReceiver.java @@ -28,7 +28,7 @@ public void run() { List registrations = database.getRegistrationsByApp(packageName); boolean deletedAll = true; for (GcmDatabase.Registration registration : registrations) { - deletedAll &= PushRegisterService.unregister(context, registration.packageName, registration.signature, null, null).deleted != null; + deletedAll &= PushRegisterManager.unregister(context, registration.packageName, registration.signature, null, null).deleted != null; } if (deletedAll) { database.removeApp(packageName); diff --git a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java b/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java index 4107eadb9e..39149ce633 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/AskPushPermission.java @@ -66,7 +66,7 @@ public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { - PushRegisterService.registerAndReply(AskPushPermission.this, intent, packageName, requestId); + PushRegisterService.registerAndReply(AskPushPermission.this, database, intent, packageName, requestId); } }).start(); finish(); diff --git a/play-services-core/src/main/java/org/microg/gms/ui/GcmAppFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/GcmAppFragment.java index 26fa5c5cb6..1735b8152c 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/GcmAppFragment.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/GcmAppFragment.java @@ -7,7 +7,6 @@ import android.net.Uri; import android.os.Bundle; import android.provider.Settings; -import android.support.annotation.StringRes; import android.support.v14.preference.SwitchPreference; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; @@ -21,7 +20,7 @@ import com.google.android.gms.R; import org.microg.gms.gcm.GcmDatabase; -import org.microg.gms.gcm.PushRegisterService; +import org.microg.gms.gcm.PushRegisterManager; import org.microg.tools.ui.AbstractSettingsActivity; import org.microg.tools.ui.ResourceSettingsFragment; @@ -185,7 +184,7 @@ public void onClick(DialogInterface dialog, int which) { @Override public void run() { for (GcmDatabase.Registration registration : registrations) { - PushRegisterService.unregister(getContext(), registration.packageName, registration.signature, null, null); + PushRegisterManager.unregister(getContext(), registration.packageName, registration.signature, null, null); } getActivity().runOnUiThread(new Runnable() { @Override