0
0
Fork 0
mirror of https://github.com/YTVanced/VancedMicroG synced 2024-11-10 13:05:05 +00:00

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
This commit is contained in:
Marvin W 2016-11-20 19:37:13 +01:00
parent 279b35b993
commit 97f4c82172
No known key found for this signature in database
GPG key ID: 072E9235DB996F2A
16 changed files with 362 additions and 56 deletions

View file

@ -26,6 +26,10 @@
android:name="com.google.android.c2dm.permission.SEND" android:name="com.google.android.c2dm.permission.SEND"
android:label="@string/perm_c2dm_send_label" android:label="@string/perm_c2dm_send_label"
android:protectionLevel="signature"/> android:protectionLevel="signature"/>
<permission
android:name="com.google.android.gtalkservice.permission.GTALK_SERVICE"
android:label="@string/perm_gtalk_svc_label"
android:protectionLevel="signature"/>
<permission-tree <permission-tree
android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" android:name="com.google.android.googleapps.permission.GOOGLE_AUTH"
@ -74,9 +78,12 @@
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/> <uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
<uses-permission android:name="com.google.android.gtalkservice.permission.GTALK_SERVICE"/>
<uses-permission android:name="org.microg.gms.STATUS_BROADCAST"/> <uses-permission android:name="org.microg.gms.STATUS_BROADCAST"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application <application
android:allowBackup="false" android:allowBackup="false"
@ -167,8 +174,6 @@
<receiver android:name="org.microg.gms.checkin.TriggerReceiver"> <receiver android:name="org.microg.gms.checkin.TriggerReceiver">
<intent-filter> <intent-filter>
<action android:name="android.server.checkin.CHECKIN"/> <action android:name="android.server.checkin.CHECKIN"/>
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
@ -200,14 +205,26 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
</service> </service>
<service
android:name="org.microg.gms.gcm.McsService" <service android:name="org.microg.gms.gcm.McsService"/>
android:exported="true"/>
<receiver
android:name="org.microg.gms.gcm.SendReceiver"
android:permission="com.google.android.c2dm.permission.RECEIVE">
<intent-filter>
<action android:name="com.google.android.gcm.intent.SEND"/>
</intent-filter>
</receiver>
<receiver android:name="org.microg.gms.gcm.TriggerReceiver"> <receiver android:name="org.microg.gms.gcm.TriggerReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<action android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"/>
<action android:name="org.microg.gms.gcm.RECONNECT"/> <action android:name="org.microg.gms.gcm.RECONNECT"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.PACKAGE_RESTARTED"/> <action android:name="android.intent.action.PACKAGE_RESTARTED"/>
</intent-filter> </intent-filter>
@ -222,7 +239,10 @@
<receiver android:name="org.microg.gms.gcm.UnregisterReceiver"> <receiver android:name="org.microg.gms.gcm.UnregisterReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter> </intent-filter>
</receiver> </receiver>

View file

@ -16,6 +16,7 @@
package org.microg.gms.common; package org.microg.gms.common;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@ -28,6 +29,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; 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_NAME;
import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1; import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1;
@ -95,6 +97,15 @@ public class PackageUtils {
return null; 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) { public static String sha1sum(byte[] bytes) {
MessageDigest md; MessageDigest md;
try { try {

View file

@ -40,5 +40,6 @@ public final class McsConstants {
public static String ACTION_CONNECT = "org.microg.gms.gcm.mcs.CONNECT"; 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_RECONNECT = "org.microg.gms.gcm.mcs.RECONNECT";
public static String ACTION_HEARTBEAT = "org.microg.gms.gcm.mcs.HEARTBEAT"; 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"; public static String EXTRA_REASON = "org.microg.gms.gcm.mcs.REASON";
} }

View file

@ -147,24 +147,29 @@ public class McsInputStream extends Thread implements Closeable {
private static Message read(int mcsTag, byte[] bytes, int len) throws IOException { private static Message read(int mcsTag, byte[] bytes, int len) throws IOException {
Wire wire = new Wire(); Wire wire = new Wire();
switch (mcsTag) { try {
case MCS_HEARTBEAT_PING_TAG: switch (mcsTag) {
return wire.parseFrom(bytes, 0, len, HeartbeatPing.class); case MCS_HEARTBEAT_PING_TAG:
case MCS_HEARTBEAT_ACK_TAG: return wire.parseFrom(bytes, 0, len, HeartbeatPing.class);
return wire.parseFrom(bytes, 0, len, HeartbeatAck.class); case MCS_HEARTBEAT_ACK_TAG:
case MCS_LOGIN_REQUEST_TAG: return wire.parseFrom(bytes, 0, len, HeartbeatAck.class);
return wire.parseFrom(bytes, 0, len, LoginRequest.class); case MCS_LOGIN_REQUEST_TAG:
case MCS_LOGIN_RESPONSE_TAG: return wire.parseFrom(bytes, 0, len, LoginRequest.class);
return wire.parseFrom(bytes, 0, len, LoginResponse.class); case MCS_LOGIN_RESPONSE_TAG:
case MCS_CLOSE_TAG: return wire.parseFrom(bytes, 0, len, LoginResponse.class);
return wire.parseFrom(bytes, 0, len, Close.class); case MCS_CLOSE_TAG:
case MCS_IQ_STANZA_TAG: return wire.parseFrom(bytes, 0, len, Close.class);
return wire.parseFrom(bytes, 0, len, IqStanza.class); case MCS_IQ_STANZA_TAG:
case MCS_DATA_MESSAGE_STANZA_TAG: return wire.parseFrom(bytes, 0, len, IqStanza.class);
return wire.parseFrom(bytes, 0, len, DataMessageStanza.class); case MCS_DATA_MESSAGE_STANZA_TAG:
default: return wire.parseFrom(bytes, 0, len, DataMessageStanza.class);
Log.w(TAG, "Unknown tag: " + mcsTag); default:
return null; Log.w(TAG, "Unknown tag: " + mcsTag);
return null;
}
} catch (IllegalStateException e) {
Log.w(TAG, "Error parsing tag: "+mcsTag, e);
return null;
} }
} }

View file

@ -25,9 +25,12 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Messenger;
import android.os.Parcelable;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver; import android.support.v4.content.WakefulBroadcastReceiver;
@ -36,6 +39,7 @@ import android.util.Log;
import com.squareup.wire.Message; import com.squareup.wire.Message;
import org.microg.gms.checkin.LastCheckinInfo; 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.AppData;
import org.microg.gms.gcm.mcs.Close; import org.microg.gms.gcm.mcs.Close;
import org.microg.gms.gcm.mcs.DataMessageStanza; import org.microg.gms.gcm.mcs.DataMessageStanza;
@ -47,19 +51,30 @@ import org.microg.gms.gcm.mcs.Setting;
import java.io.Closeable; import java.io.Closeable;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import okio.ByteString;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.os.Build.VERSION.SDK_INT; 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.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_COLLAPSE_KEY;
import static org.microg.gms.gcm.GcmConstants.EXTRA_FROM; 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_CONNECT;
import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; 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_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.EXTRA_REASON;
import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG; import static org.microg.gms.gcm.McsConstants.MCS_CLOSE_TAG;
import static org.microg.gms.gcm.McsConstants.MCS_DATA_MESSAGE_STANZA_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 Intent connectIntent;
private static int maxTtl = 24 * 60 * 60;
private class HandlerThread extends Thread { private class HandlerThread extends Thread {
public HandlerThread() { public HandlerThread() {
@ -140,6 +157,7 @@ public class McsService extends Service implements Handler.Callback {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
TriggerReceiver.register(this);
database = new GcmDatabase(this); database = new GcmDatabase(this);
heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0); heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); 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 @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return null; return null;
@ -221,6 +245,8 @@ public class McsService extends Service implements Handler.Callback {
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, reason)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, reason));
} else if (ACTION_HEARTBEAT.equals(intent.getAction())) { } else if (ACTION_HEARTBEAT.equals(intent.getAction())) {
rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason));
} else if (ACTION_SEND.equals(intent.getAction())) {
handleSendMessage(intent);
} }
WakefulBroadcastReceiver.completeWakefulIntent(intent); WakefulBroadcastReceiver.completeWakefulIntent(intent);
} else if (connectIntent == null) { } else if (connectIntent == null) {
@ -232,6 +258,94 @@ public class McsService extends Service implements Handler.Callback {
return START_REDELIVER_INTENT; 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> 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() { private synchronized void connect() {
try { try {
tryClose(inputStream); tryClose(inputStream);
@ -319,7 +433,11 @@ public class McsService extends Service implements Handler.Callback {
intent.setAction(ACTION_C2DM_RECEIVE); intent.setAction(ACTION_C2DM_RECEIVE);
intent.setPackage(msg.category); intent.setPackage(msg.category);
intent.putExtra(EXTRA_FROM, msg.from); 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); if (msg.token != null) intent.putExtra(EXTRA_COLLAPSE_KEY, msg.token);
for (AppData appData : msg.app_data) { for (AppData appData : msg.app_data) {
intent.putExtra(appData.key, appData.value); intent.putExtra(appData.key, appData.value);

View file

@ -16,18 +16,14 @@
package org.microg.gms.gcm; package org.microg.gms.gcm;
import android.app.AlertDialog;
import android.app.IntentService; import android.app.IntentService;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.text.Html;
import android.util.Log; import android.util.Log;
import org.microg.gms.checkin.CheckinService; import org.microg.gms.checkin.CheckinService;
@ -38,10 +34,6 @@ import org.microg.gms.ui.AskPushPermission;
import java.io.IOException; 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_REGISTER;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER;
@ -74,6 +66,12 @@ public class PushRegisterService extends IntentService {
database = new GcmDatabase(this); 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) { public static RegisterResponse register(Context context, String packageName, String pkgSignature, String sender, String info) {
GcmDatabase database = new GcmDatabase(context); GcmDatabase database = new GcmDatabase(context);
RegisterResponse response = register(context, packageName, pkgSignature, sender, info, false); RegisterResponse response = register(context, packageName, pkgSignature, sender, info, false);
@ -83,6 +81,7 @@ public class PushRegisterService extends IntentService {
} else { } else {
database.noteAppRegistrationError(packageName, response.responseText); database.noteAppRegistrationError(packageName, response.responseText);
} }
database.close();
return response; return response;
} }
@ -94,12 +93,14 @@ public class PushRegisterService extends IntentService {
} else { } else {
database.noteAppUnregistered(packageName, pkgSignature); database.noteAppUnregistered(packageName, pkgSignature);
} }
database.close();
return response; return response;
} }
@Override @Override
protected void onHandleIntent(Intent intent) { protected void onHandleIntent(Intent intent) {
Log.d(TAG, "onHandleIntent: " + intent); Log.d(TAG, "onHandleIntent: " + intent);
Log.d(TAG, "onHandleIntent: " + intent.getExtras());
if (LastCheckinInfo.read(this).lastCheckin > 0) { if (LastCheckinInfo.read(this).lastCheckin > 0) {
try { try {
if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) || 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) { private void register(final Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); 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()); Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras());
GcmDatabase.App app = database.getApp(packageName); GcmDatabase.App app = database.getApp(packageName);
@ -214,7 +206,7 @@ public class PushRegisterService extends IntentService {
private void unregister(Intent intent) { private void unregister(Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP); 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()); Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.getExtras());
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION); Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);

View file

@ -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);
}
}

View file

@ -18,20 +18,35 @@ package org.microg.gms.gcm;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.preference.PreferenceManager;
import android.support.v4.content.WakefulBroadcastReceiver; import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log; import android.util.Log;
import org.microg.gms.checkin.LastCheckinInfo; 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_CONNECT;
import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT; import static org.microg.gms.gcm.McsConstants.ACTION_HEARTBEAT;
import static org.microg.gms.gcm.McsConstants.EXTRA_REASON; import static org.microg.gms.gcm.McsConstants.EXTRA_REASON;
public class TriggerReceiver extends WakefulBroadcastReceiver { public class TriggerReceiver extends WakefulBroadcastReceiver {
private static final String TAG = "GmsGcmTrigger"; 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 @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -48,9 +63,9 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
} }
NetworkInfo networkInfo = cm.getActiveNetworkInfo(); 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) { 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) startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class)
.putExtra(EXTRA_REASON, intent)); .putExtra(EXTRA_REASON, intent));
} else { } else {

View file

@ -5,6 +5,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.Log; import android.util.Log;
import java.util.List;
import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_DATA_REMOVED; import static android.content.Intent.EXTRA_DATA_REMOVED;
@ -12,10 +14,31 @@ public class UnregisterReceiver extends BroadcastReceiver {
private static final String TAG = "GmsGcmUnregisterRcvr"; private static final String TAG = "GmsGcmUnregisterRcvr";
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(final Context context, Intent intent) {
Log.d(TAG, "Package removed: " + intent); Log.d(TAG, "Package changed: " + intent);
if (ACTION_PACKAGE_REMOVED.contains(intent.getAction()) && intent.getBooleanExtra(EXTRA_DATA_REMOVED, false)) { 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<GcmDatabase.Registration> 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();
}
} }
} }
} }

View file

@ -84,5 +84,6 @@ public class AskPushPermission extends FragmentActivity {
PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName); PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName);
answered = true; answered = true;
} }
database.close();
} }
} }

View file

@ -82,6 +82,12 @@ public class GcmAppFragment extends ResourceSettingsFragment {
updateAppDetails(); updateAppDetails();
} }
@Override
public void onPause() {
super.onPause();
database.close();
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@ -113,6 +119,39 @@ public class GcmAppFragment extends ResourceSettingsFragment {
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue instanceof Boolean) { if (newValue instanceof Boolean) {
if (!(boolean) newValue) {
final List<GcmDatabase.Registration> 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); database.setAppAllowRegister(packageName, (Boolean) newValue);
return true; return true;
} }
@ -169,7 +208,7 @@ public class GcmAppFragment extends ResourceSettingsFragment {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
// Do nothing // Do nothing
} }
}).create().show(); }).show();
return true; return true;
} }
}); });

View file

@ -115,6 +115,7 @@ public class GcmFragment extends ResourceSettingsFragment implements SwitchBar.O
listenerSetup = false; listenerSetup = false;
} }
super.onPause(); super.onPause();
database.close();
} }
@Override @Override
@ -225,6 +226,7 @@ public class GcmFragment extends ResourceSettingsFragment implements SwitchBar.O
} else { } else {
setSummary(R.string.gcm_no_message_yet); setSummary(R.string.gcm_no_message_yet);
} }
database.close();
} }
@Override @Override

View file

@ -56,7 +56,9 @@ public class SettingsActivity extends AbstractDashboardActivity {
PreferenceManager prefs = getPreferenceManager(); PreferenceManager prefs = getPreferenceManager();
prefs.findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext()))); prefs.findPreference(PREF_ABOUT).setSummary(getString(R.string.about_version_str, AboutFragment.getSelfVersion(getContext())));
if (GcmPrefs.get(getContext()).isGcmEnabled()) { 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)); prefs.findPreference(PREF_GCM).setSummary(getString(R.string.v7_preference_on) + " / " + getContext().getString(R.string.gcm_registered_apps_counter, regCount));
} else { } else {
prefs.findPreference(PREF_GCM).setSummary(R.string.v7_preference_off); prefs.findPreference(PREF_GCM).setSummary(R.string.v7_preference_off);

View file

@ -6,8 +6,10 @@ import com.squareup.wire.Message;
import com.squareup.wire.ProtoField; import com.squareup.wire.ProtoField;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import okio.ByteString;
import static com.squareup.wire.Message.Datatype.BOOL; 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.INT32;
import static com.squareup.wire.Message.Datatype.INT64; import static com.squareup.wire.Message.Datatype.INT64;
import static com.squareup.wire.Message.Datatype.STRING; 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 Long DEFAULT_SENT = 0L;
public static final Integer DEFAULT_QUEUED = 0; public static final Integer DEFAULT_QUEUED = 0;
public static final Long DEFAULT_STATUS = 0L; 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. * Not used.
@ -159,7 +163,13 @@ public final class DataMessageStanza extends Message {
@ProtoField(tag = 20, type = INT64) @ProtoField(tag = 20, type = INT64)
public final Long status; public final Long status;
public DataMessageStanza(Long rmq_id, String id, String from, String to, String category, String token, List<AppData> 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<AppData> 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.rmq_id = rmq_id;
this.id = id; this.id = id;
this.from = from; this.from = from;
@ -180,10 +190,12 @@ public final class DataMessageStanza extends Message {
this.sent = sent; this.sent = sent;
this.queued = queued; this.queued = queued;
this.status = status; this.status = status;
this.raw_data = raw_data;
this.delay = delay;
} }
private DataMessageStanza(Builder builder) { 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); setBuilder(builder);
} }
@ -211,7 +223,9 @@ public final class DataMessageStanza extends Message {
&& equals(ttl, o.ttl) && equals(ttl, o.ttl)
&& equals(sent, o.sent) && equals(sent, o.sent)
&& equals(queued, o.queued) && equals(queued, o.queued)
&& equals(status, o.status); && equals(status, o.status)
&& equals(raw_data, o.raw_data)
&& equals(delay, o.delay);
} }
@Override @Override
@ -238,6 +252,8 @@ public final class DataMessageStanza extends Message {
result = result * 37 + (sent != null ? sent.hashCode() : 0); result = result * 37 + (sent != null ? sent.hashCode() : 0);
result = result * 37 + (queued != null ? queued.hashCode() : 0); result = result * 37 + (queued != null ? queued.hashCode() : 0);
result = result * 37 + (status != null ? status.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; hashCode = result;
} }
return result; return result;
@ -265,6 +281,8 @@ public final class DataMessageStanza extends Message {
public Long sent; public Long sent;
public Integer queued; public Integer queued;
public Long status; public Long status;
public ByteString raw_data;
public Integer delay;
public Builder() { public Builder() {
} }
@ -292,6 +310,8 @@ public final class DataMessageStanza extends Message {
this.sent = message.sent; this.sent = message.sent;
this.queued = message.queued; this.queued = message.queued;
this.status = message.status; 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; 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 @Override
public DataMessageStanza build() { public DataMessageStanza build() {
checkRequiredFields(); checkRequiredFields();

View file

@ -247,6 +247,10 @@ message DataMessageStanza {
optional int32 queued = 19; optional int32 queued = 19;
optional int64 status = 20; optional int64 status = 20;
optional bytes raw_data = 21;
optional int32 delay = 22;
} }
/** /**

View file

@ -42,6 +42,7 @@ This can take a couple of minutes."</string>
<string name="perm_status_broadcast_label">listen to internal status broadcasts</string> <string name="perm_status_broadcast_label">listen to internal status broadcasts</string>
<string name="perm_c2dm_receive_label">listen to C2DM messages</string> <string name="perm_c2dm_receive_label">listen to C2DM messages</string>
<string name="perm_c2dm_send_label">send C2DM messages to other apps</string> <string name="perm_c2dm_send_label">send C2DM messages to other apps</string>
<string name="perm_gtalk_svc_label">exchange messages and receive sync notifications from Google servers</string>
<string name="pref_auth_trust_google_title">Trust Google for app permissions</string> <string name="pref_auth_trust_google_title">Trust Google for app permissions</string>
<string name="pref_auth_trust_google_summary">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.</string> <string name="pref_auth_trust_google_summary">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.</string>
@ -118,6 +119,7 @@ This can take a couple of minutes."</string>
<string name="gcm_registered_since">Registered since: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string> <string name="gcm_registered_since">Registered since: <xliff:g example="Yesterday, 02:20 PM">%1$s</xliff:g></string>
<string name="gcm_unregister_confirm_title">Unregister <xliff:g example="F-Droid">%1$s</xliff:g>?</string> <string name="gcm_unregister_confirm_title">Unregister <xliff:g example="F-Droid">%1$s</xliff:g>?</string>
<string name="gcm_unregister_confirm_message">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?</string> <string name="gcm_unregister_confirm_message">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?</string>
<string name="gcm_unregister_after_deny_message">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?</string>
<string name="gcm_messages_counter">Messages: <xliff:g example="123">%1$d</xliff:g> (<xliff:g example="12345">%2$d</xliff:g> bytes)</string> <string name="gcm_messages_counter">Messages: <xliff:g example="123">%1$d</xliff:g> (<xliff:g example="12345">%2$d</xliff:g> bytes)</string>
<string name="gcm_state_disconnected">Current State: Disconnected</string> <string name="gcm_state_disconnected">Current State: Disconnected</string>
<string name="gcm_state_connected">Current State: Connected since <xliff:g example="2 hours ago">%1$s</xliff:g></string> <string name="gcm_state_connected">Current State: Connected since <xliff:g example="2 hours ago">%1$s</xliff:g></string>
@ -136,6 +138,4 @@ This can take a couple of minutes."</string>
<string name="network_type_wifi">Wi-Fi</string> <string name="network_type_wifi">Wi-Fi</string>
<string name="network_type_roaming">Roaming</string> <string name="network_type_roaming">Roaming</string>
<string name="network_type_other">Other networks</string> <string name="network_type_other">Other networks</string>
</resources> </resources>