Skip to content

Commit

Permalink
Push Messaging: Handle more edge cases, fix some apps not showing up …
Browse files Browse the repository at this point in the history
…as registered
  • Loading branch information
mar-v-in committed Sep 21, 2018
1 parent e471018 commit 719cd51
Show file tree
Hide file tree
Showing 11 changed files with 467 additions and 264 deletions.
10 changes: 6 additions & 4 deletions play-services-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,18 @@
android:permission="com.google.android.c2dm.permission.RECEIVE">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTER"/>

<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.UNREGISTER"/>

<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>

<receiver android:name="org.microg.gms.gcm.PushRegisterReceiver">
<intent-filter>
<action android:name="com.google.iid.TOKEN_REQUEST"/>
</intent-filter>
</receiver>

<service android:name="org.microg.gms.gcm.McsService"/>

<receiver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ public static <T> T request(String url, Request request, Class<T> 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()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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<RegisterResponse>() {
@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;
}
}
Loading

0 comments on commit 719cd51

Please sign in to comment.