From 97f4c82172a03011e6198b652162827ba6d24da7 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 20 Nov 2016 19:37:13 +0100 Subject: [PATCH] GCM improvements - Add upstream message support (#228) - Improve support for 7.0+ (#226) - don't start closed apps if disabled (#230) - ask after denying registered app (#230) - automatically unregister apps on uninstall --- .../src/main/AndroidManifest.xml | 30 ++++- .../org/microg/gms/common/PackageUtils.java | 11 ++ .../java/org/microg/gms/gcm/McsConstants.java | 1 + .../org/microg/gms/gcm/McsInputStream.java | 41 +++--- .../java/org/microg/gms/gcm/McsService.java | 120 +++++++++++++++++- .../microg/gms/gcm/PushRegisterService.java | 30 ++--- .../java/org/microg/gms/gcm/SendReceiver.java | 43 +++++++ .../org/microg/gms/gcm/TriggerReceiver.java | 21 ++- .../microg/gms/gcm/UnregisterReceiver.java | 29 ++++- .../org/microg/gms/ui/AskPushPermission.java | 1 + .../org/microg/gms/ui/GcmAppFragment.java | 41 +++++- .../java/org/microg/gms/ui/GcmFragment.java | 2 + .../org/microg/gms/ui/SettingsActivity.java | 4 +- .../microg/gms/gcm/mcs/DataMessageStanza.java | 36 +++++- .../src/main/protos-repo/mcs.proto | 4 + .../src/main/res/values/strings.xml | 4 +- 16 files changed, 362 insertions(+), 56 deletions(-) create mode 100644 play-services-core/src/main/java/org/microg/gms/gcm/SendReceiver.java diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index ba244228..b41299fe 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -26,6 +26,10 @@ android:name="com.google.android.c2dm.permission.SEND" android:label="@string/perm_c2dm_send_label" android:protectionLevel="signature"/> + + + + - - @@ -200,14 +205,26 @@ - + + + + + + + + + + + + + @@ -222,7 +239,10 @@ + + + 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 0b25758e..b1456c34 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 @@ -16,6 +16,7 @@ package org.microg.gms.common; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -28,6 +29,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME; import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; @@ -95,6 +97,15 @@ public class PackageUtils { return null; } + @SuppressWarnings("deprecation") + public static String packageFromPendingIntent(PendingIntent pi) { + if (SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + return pi.getTargetPackage(); + } else { + return pi.getCreatorPackage(); + } + } + public static String sha1sum(byte[] bytes) { MessageDigest md; try { diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java index 49f42ea9..aac34ad9 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsConstants.java @@ -40,5 +40,6 @@ public final class McsConstants { public static String ACTION_CONNECT = "org.microg.gms.gcm.mcs.CONNECT"; public static String ACTION_RECONNECT = "org.microg.gms.gcm.mcs.RECONNECT"; public static String ACTION_HEARTBEAT = "org.microg.gms.gcm.mcs.HEARTBEAT"; + public static String ACTION_SEND = "org.microg.gms.gcm.mcs.SEND"; public static String EXTRA_REASON = "org.microg.gms.gcm.mcs.REASON"; } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsInputStream.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsInputStream.java index 776e3f67..0001be6c 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsInputStream.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsInputStream.java @@ -147,24 +147,29 @@ public class McsInputStream extends Thread implements Closeable { private static Message read(int mcsTag, byte[] bytes, int len) throws IOException { Wire wire = new Wire(); - switch (mcsTag) { - case MCS_HEARTBEAT_PING_TAG: - return wire.parseFrom(bytes, 0, len, HeartbeatPing.class); - 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: - Log.w(TAG, "Unknown tag: " + mcsTag); - return null; + try { + switch (mcsTag) { + case MCS_HEARTBEAT_PING_TAG: + return wire.parseFrom(bytes, 0, len, HeartbeatPing.class); + 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: + Log.w(TAG, "Unknown tag: " + mcsTag); + return null; + } + } catch (IllegalStateException e) { + Log.w(TAG, "Error parsing tag: "+mcsTag, e); + return null; } } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index e1f416ea..77a1ce8c 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -25,9 +25,12 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Messenger; +import android.os.Parcelable; import android.os.PowerManager; import android.os.SystemClock; import android.support.v4.content.WakefulBroadcastReceiver; @@ -36,6 +39,7 @@ import android.util.Log; import com.squareup.wire.Message; import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.PackageUtils; import org.microg.gms.gcm.mcs.AppData; import org.microg.gms.gcm.mcs.Close; import org.microg.gms.gcm.mcs.DataMessageStanza; @@ -47,19 +51,30 @@ import org.microg.gms.gcm.mcs.Setting; import java.io.Closeable; import java.net.Socket; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.net.ssl.SSLContext; +import okio.ByteString; + import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.os.Build.VERSION.SDK_INT; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; +import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY; import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSAGE_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER; +import static org.microg.gms.gcm.GcmConstants.EXTRA_REGISTRATION_ID; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_FROM; +import static org.microg.gms.gcm.GcmConstants.EXTRA_SEND_TO; +import static org.microg.gms.gcm.GcmConstants.EXTRA_TTL; import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT; import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; import static org.microg.gms.gcm.McsConstants.ACTION_RECONNECT; +import static org.microg.gms.gcm.McsConstants.ACTION_SEND; import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG; import static org.microg.gms.gcm.McsConstants.MCS_DATA_MESSAGE_STANZA_TAG; @@ -111,6 +126,8 @@ public class McsService extends Service implements Handler.Callback { private Intent connectIntent; + private static int maxTtl = 24 * 60 * 60; + private class HandlerThread extends Thread { public HandlerThread() { @@ -140,6 +157,7 @@ public class McsService extends Service implements Handler.Callback { @Override public void onCreate() { super.onCreate(); + TriggerReceiver.register(this); database = new GcmDatabase(this); heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); @@ -152,6 +170,12 @@ public class McsService extends Service implements Handler.Callback { } } + @Override + public void onDestroy() { + database.close(); + super.onDestroy(); + } + @Override public IBinder onBind(Intent intent) { return null; @@ -221,6 +245,8 @@ public class McsService extends Service implements Handler.Callback { rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, reason)); } else if (ACTION_HEARTBEAT.equals(intent.getAction())) { rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason)); + } else if (ACTION_SEND.equals(intent.getAction())) { + handleSendMessage(intent); } WakefulBroadcastReceiver.completeWakefulIntent(intent); } else if (connectIntent == null) { @@ -232,6 +258,94 @@ public class McsService extends Service implements Handler.Callback { return START_REDELIVER_INTENT; } + private void handleSendMessage(Intent intent) { + String messageId = intent.getStringExtra(EXTRA_MESSAGE_ID); + String collapseKey = intent.getStringExtra(EXTRA_COLLAPSE_KEY); + + Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); + intent.removeExtra(EXTRA_MESSENGER); + + Parcelable app = intent.getParcelableExtra(EXTRA_APP); + String packageName = null; + if (app instanceof PendingIntent) { + packageName = PackageUtils.packageFromPendingIntent((PendingIntent) app); + } + if (packageName == null) { + Log.w(TAG, "Failed to send message, missing package name"); + return; + } + intent.removeExtra(EXTRA_APP); + + int ttl; + try { + ttl = Integer.parseInt(intent.getStringExtra(EXTRA_TTL)); + if (ttl < 0 || ttl > maxTtl) { + ttl = maxTtl; + } + } catch (NumberFormatException e) { + // TODO: error TtlUnsupported + Log.w(TAG, e); + return; + } + + String to = intent.getStringExtra(EXTRA_SEND_TO); + if (to == null) { + // TODO: error missing_to + Log.w(TAG, "missing to"); + return; + } + + String from = intent.getStringExtra(EXTRA_SEND_FROM); + if (from != null) { + intent.removeExtra(EXTRA_SEND_FROM); + } else { + from = intent.getStringExtra(EXTRA_FROM); + } + if (from == null) { + GcmDatabase.Registration reg = database.getRegistration(packageName, PackageUtils.firstSignatureDigest(this, packageName)); + if (reg != null) from = reg.registerId; + } + if (from == null) { + Log.e(TAG, "Can't send message, missing from!"); + return; + } + + String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID); + intent.removeExtra(EXTRA_REGISTRATION_ID); + + List appData = new ArrayList<>(); + Bundle extras = intent.getExtras(); + for (String key : extras.keySet()) { + if (!key.startsWith("google.")) { + Object val = extras.get(key); + if (val instanceof String) { + appData.add(new AppData(key, (String) val)); + } + } + } + + byte[] rawDataArray = intent.getByteArrayExtra("rawData"); + ByteString rawData = rawDataArray != null ? ByteString.of(rawDataArray) : null; + + try { + DataMessageStanza msg = new DataMessageStanza.Builder() + .sent(System.currentTimeMillis() / 1000L) + .id(messageId) + .token(collapseKey) + .from(from) + .reg_id(registrationId) + .to(to) + .category(packageName) + .raw_data(rawData) + .app_data(appData).build(); + + send(MCS_DATA_MESSAGE_STANZA_TAG, msg); + database.noteAppMessage(packageName, msg.getSerializedSize()); + } catch (Exception e) { + Log.w(TAG, e); + } + } + private synchronized void connect() { try { tryClose(inputStream); @@ -319,7 +433,11 @@ public class McsService extends Service implements Handler.Callback { intent.setAction(ACTION_C2DM_RECEIVE); intent.setPackage(msg.category); intent.putExtra(EXTRA_FROM, msg.from); - if (app.wakeForDelivery) intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + if (app.wakeForDelivery) { + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + } else { + intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); + } if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token); for (AppData appData : msg.app_data) { intent.putExtra(appData.key, appData.value); 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 3ddfca74..b844ee13 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 @@ -16,18 +16,14 @@ package org.microg.gms.gcm; -import android.app.AlertDialog; import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.os.Build; import android.os.Message; import android.os.Messenger; -import android.text.Html; import android.util.Log; import org.microg.gms.checkin.CheckinService; @@ -38,10 +34,6 @@ import org.microg.gms.ui.AskPushPermission; import java.io.IOException; -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; -import static android.os.Build.VERSION_CODES.JELLY_BEAN; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; 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; @@ -74,6 +66,12 @@ public class PushRegisterService extends IntentService { database = new GcmDatabase(this); } + @Override + public void onDestroy() { + super.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); @@ -83,6 +81,7 @@ public class PushRegisterService extends IntentService { } else { database.noteAppRegistrationError(packageName, response.responseText); } + database.close(); return response; } @@ -94,12 +93,14 @@ public class PushRegisterService extends IntentService { } else { database.noteAppUnregistered(packageName, pkgSignature); } + database.close(); return response; } @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent: " + intent); + Log.d(TAG, "onHandleIntent: " + intent.getExtras()); if (LastCheckinInfo.read(this).lastCheckin > 0) { try { if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) || @@ -121,18 +122,9 @@ public class PushRegisterService extends IntentService { } } - @SuppressWarnings("deprecation") - private String packageFromPendingIntent(PendingIntent pi) { - if (SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { - return pi.getTargetPackage(); - } else { - return pi.getCreatorPackage(); - } - } - private void register(final Intent intent) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); - final String packageName = packageFromPendingIntent(pendingIntent); + final String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras()); GcmDatabase.App app = database.getApp(packageName); @@ -214,7 +206,7 @@ public class PushRegisterService extends IntentService { private void unregister(Intent intent) { PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); - String packageName = packageFromPendingIntent(pendingIntent); + String packageName = PackageUtils.packageFromPendingIntent(pendingIntent); Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.getExtras()); Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/SendReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/SendReceiver.java new file mode 100644 index 00000000..c37f7c2e --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/gcm/SendReceiver.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013-2016 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.os.Bundle; +import android.support.v4.content.WakefulBroadcastReceiver; +import android.util.Log; + +import static org.microg.gms.gcm.McsConstants.ACTION_SEND; + +public class SendReceiver extends WakefulBroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getExtras() == null) return; + Bundle extras = intent.getExtras(); + Log.d("GmsMcsSendRcvr", "original extras: " + extras); + for (String key : extras.keySet()) { + if (key.startsWith("GOOG.") || key.startsWith("GOOGLE.")) { + extras.remove(key); + } + } + Intent i = new Intent(context, McsService.class); + i.setAction(ACTION_SEND); + i.putExtras(extras); + startWakefulService(context, i); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java index ecee6aa7..5d572240 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java @@ -18,20 +18,35 @@ package org.microg.gms.gcm; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.preference.PreferenceManager; import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; import org.microg.gms.checkin.LastCheckinInfo; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.N; import static org.microg.gms.gcm.McsConstants.ACTION_CONNECT; import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; public class TriggerReceiver extends WakefulBroadcastReceiver { private static final String TAG = "GmsGcmTrigger"; + private static boolean registered = false; + + /** + * "Project Svelte" is just there to f**k things up... + */ + public synchronized static void register(Context context) { + if (SDK_INT >= N && !registered) { + IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); + context.registerReceiver(new TriggerReceiver(), intentFilter); + registered = true; + } + } + @Override public void onReceive(Context context, Intent intent) { @@ -48,9 +63,9 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { } NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - if (networkInfo != null && networkInfo.isConnected() || force) { + if (networkInfo != null && networkInfo.isConnected() || force || "android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) { if (!McsService.isConnected() || force) { - Log.d(TAG, "Not connected to GCM but should be, asking the service to start up"); + Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent); startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class) .putExtra(EXTRA_REASON, intent)); } else { 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 6900876e..5496ce02 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 @@ -5,6 +5,8 @@ import android.content.Context; import android.content.Intent; import android.util.Log; +import java.util.List; + import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.EXTRA_DATA_REMOVED; @@ -12,10 +14,31 @@ public class UnregisterReceiver extends BroadcastReceiver { private static final String TAG = "GmsGcmUnregisterRcvr"; @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "Package removed: " + intent); + public void onReceive(final Context context, Intent intent) { + Log.d(TAG, "Package changed: " + intent); if (ACTION_PACKAGE_REMOVED.contains(intent.getAction()) && intent.getBooleanExtra(EXTRA_DATA_REMOVED, false)) { - Log.d(TAG, "Package removed: " + intent.getData()); + final GcmDatabase database = new GcmDatabase(context); + final String packageName = intent.getData().getSchemeSpecificPart(); + Log.d(TAG, "Package removed: " + packageName); + final GcmDatabase.App app = database.getApp(packageName); + if (app != null) { + new Thread(new Runnable() { + @Override + 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; + } + if (deletedAll) { + database.removeApp(packageName); + } + database.close(); + } + }).start(); + } else { + database.close(); + } } } } 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 570275d8..c3a5da5f 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 @@ -84,5 +84,6 @@ public class AskPushPermission extends FragmentActivity { PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName); answered = true; } + database.close(); } } 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 2b413823..a8887f1b 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 @@ -82,6 +82,12 @@ public class GcmAppFragment extends ResourceSettingsFragment { updateAppDetails(); } + @Override + public void onPause() { + super.onPause(); + database.close(); + } + @Override public void onResume() { super.onResume(); @@ -113,6 +119,39 @@ public class GcmAppFragment extends ResourceSettingsFragment { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (newValue instanceof Boolean) { + if (!(boolean) newValue) { + final List registrations = database.getRegistrationsByApp(packageName); + if (!registrations.isEmpty()) { + new AlertDialog.Builder(getContext()) + .setTitle(String.format(getString(R.string.gcm_unregister_confirm_title), appName)) + .setMessage(R.string.gcm_unregister_after_deny_message) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + new Thread(new Runnable() { + @Override + public void run() { + for (GcmDatabase.Registration registration : registrations) { + PushRegisterService.unregister(getContext(), registration.packageName, registration.signature, null, null); + } + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + updateAppDetails(); + } + }); + } + }).start(); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }).show(); + } + } database.setAppAllowRegister(packageName, (Boolean) newValue); return true; } @@ -169,7 +208,7 @@ public class GcmAppFragment extends ResourceSettingsFragment { public void onClick(DialogInterface dialog, int which) { // Do nothing } - }).create().show(); + }).show(); return true; } }); diff --git a/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java b/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java index 5f032396..6381e1dd 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/GcmFragment.java @@ -115,6 +115,7 @@ public class GcmFragment extends ResourceSettingsFragment implements SwitchBar.O listenerSetup = false; } super.onPause(); + database.close(); } @Override @@ -225,6 +226,7 @@ public class GcmFragment extends ResourceSettingsFragment implements SwitchBar.O } else { setSummary(R.string.gcm_no_message_yet); } + database.close(); } @Override diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java index f9d6007f..44bd623f 100644 --- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java @@ -56,7 +56,9 @@ public class SettingsActivity extends AbstractDashboardActivity { PreferenceManager prefs = getPreferenceManager(); prefs.findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext()))); if (GcmPrefs.get(getContext()).isGcmEnabled()) { - int regCount = new GcmDatabase(getContext()).getRegistrationList().size(); + GcmDatabase database = new GcmDatabase(getContext()); + int regCount = database.getRegistrationList().size(); + database.close(); prefs.findPreference(PREF_GCM).setSummary(getString(R.string.v7_preference_on) + " / " + getContext().getString(R.string.gcm_registered_apps_counter, regCount)); } else { prefs.findPreference(PREF_GCM).setSummary(R.string.v7_preference_off); 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 index 89d62c4b..b8407dd7 100644 --- 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 @@ -6,8 +6,10 @@ import com.squareup.wire.Message; import com.squareup.wire.ProtoField; import java.util.Collections; import java.util.List; +import okio.ByteString; import static com.squareup.wire.Message.Datatype.BOOL; +import static com.squareup.wire.Message.Datatype.BYTES; import static com.squareup.wire.Message.Datatype.INT32; import static com.squareup.wire.Message.Datatype.INT64; import static com.squareup.wire.Message.Datatype.STRING; @@ -39,6 +41,8 @@ public final class DataMessageStanza extends Message { public static final Long DEFAULT_SENT = 0L; public static final Integer DEFAULT_QUEUED = 0; public static final Long DEFAULT_STATUS = 0L; + public static final ByteString DEFAULT_RAW_DATA = ByteString.EMPTY; + public static final Integer DEFAULT_DELAY = 0; /** * Not used. @@ -159,7 +163,13 @@ public final class DataMessageStanza extends Message { @ProtoField(tag = 20, type = INT64) public final Long status; - public DataMessageStanza(Long rmq_id, 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 permission, String reg_id, String pkg_signature, String client_id, Long device_user_id, Integer ttl, Long sent, Integer queued, Long status) { + @ProtoField(tag = 21, type = BYTES) + public final ByteString raw_data; + + @ProtoField(tag = 22, type = INT32) + public final Integer delay; + + public DataMessageStanza(Long rmq_id, 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 permission, String reg_id, String pkg_signature, String client_id, Long device_user_id, Integer ttl, Long sent, Integer queued, Long status, ByteString raw_data, Integer delay) { this.rmq_id = rmq_id; this.id = id; this.from = from; @@ -180,10 +190,12 @@ public final class DataMessageStanza extends Message { this.sent = sent; this.queued = queued; this.status = status; + this.raw_data = raw_data; + this.delay = delay; } private DataMessageStanza(Builder builder) { - this(builder.rmq_id, 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.permission, builder.reg_id, builder.pkg_signature, builder.client_id, builder.device_user_id, builder.ttl, builder.sent, builder.queued, builder.status); + this(builder.rmq_id, 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.permission, builder.reg_id, builder.pkg_signature, builder.client_id, builder.device_user_id, builder.ttl, builder.sent, builder.queued, builder.status, builder.raw_data, builder.delay); setBuilder(builder); } @@ -211,7 +223,9 @@ public final class DataMessageStanza extends Message { && equals(ttl, o.ttl) && equals(sent, o.sent) && equals(queued, o.queued) - && equals(status, o.status); + && equals(status, o.status) + && equals(raw_data, o.raw_data) + && equals(delay, o.delay); } @Override @@ -238,6 +252,8 @@ public final class DataMessageStanza extends Message { result = result * 37 + (sent != null ? sent.hashCode() : 0); result = result * 37 + (queued != null ? queued.hashCode() : 0); result = result * 37 + (status != null ? status.hashCode() : 0); + result = result * 37 + (raw_data != null ? raw_data.hashCode() : 0); + result = result * 37 + (delay != null ? delay.hashCode() : 0); hashCode = result; } return result; @@ -265,6 +281,8 @@ public final class DataMessageStanza extends Message { public Long sent; public Integer queued; public Long status; + public ByteString raw_data; + public Integer delay; public Builder() { } @@ -292,6 +310,8 @@ public final class DataMessageStanza extends Message { this.sent = message.sent; this.queued = message.queued; this.status = message.status; + this.raw_data = message.raw_data; + this.delay = message.delay; } /** @@ -453,6 +473,16 @@ public final class DataMessageStanza extends Message { return this; } + public Builder raw_data(ByteString raw_data) { + this.raw_data = raw_data; + return this; + } + + public Builder delay(Integer delay) { + this.delay = delay; + return this; + } + @Override public DataMessageStanza build() { checkRequiredFields(); diff --git a/play-services-core/src/main/protos-repo/mcs.proto b/play-services-core/src/main/protos-repo/mcs.proto index b6da3076..590f7138 100644 --- a/play-services-core/src/main/protos-repo/mcs.proto +++ b/play-services-core/src/main/protos-repo/mcs.proto @@ -247,6 +247,10 @@ message DataMessageStanza { optional int32 queued = 19; optional int64 status = 20; + + optional bytes raw_data = 21; + + optional int32 delay = 22; } /** diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index 50c2317f..dfbed0a0 100644 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -42,6 +42,7 @@ This can take a couple of minutes." listen to internal status broadcasts listen to C2DM messages send C2DM messages to other apps + exchange messages and receive sync notifications from Google servers Trust Google for app permissions When disabled, the user is asked before an apps authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled. @@ -118,6 +119,7 @@ This can take a couple of minutes." Registered since: %1$s Unregister %1$s? Some apps do not automatically re-register and/or do not provide an option to do so manually. These apps might not work correctly after unregistering.\nContinue? + You denied an app to register for push notifications that is already registered.\nDo you want to unregister it now so it does not receive push messages in the future? Messages: %1$d (%2$d bytes) Current State: Disconnected Current State: Connected since %1$s @@ -136,6 +138,4 @@ This can take a couple of minutes." Wi-Fi Roaming Other networks - -