Loading play-services-core/src/main/AndroidManifest.xml +6 −4 Original line number Diff line number Diff line Loading @@ -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 Loading play-services-core/src/main/java/org/microg/gms/common/HttpFormClient.java +7 −1 Original line number Diff line number Diff line Loading @@ -81,7 +81,13 @@ public class HttpFormClient { 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())); Loading play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +8 −0 Original line number Diff line number Diff line Loading @@ -155,4 +155,12 @@ public class PackageUtils { return -1; } } public static String versionName(Context context, String packageName) { try { return context.getPackageManager().getPackageInfo(packageName, 0).versionName; } catch (PackageManager.NameNotFoundException e) { return null; } } } play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * 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)); } } play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * 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
play-services-core/src/main/AndroidManifest.xml +6 −4 Original line number Diff line number Diff line Loading @@ -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 Loading
play-services-core/src/main/java/org/microg/gms/common/HttpFormClient.java +7 −1 Original line number Diff line number Diff line Loading @@ -81,7 +81,13 @@ public class HttpFormClient { 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())); Loading
play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +8 −0 Original line number Diff line number Diff line Loading @@ -155,4 +155,12 @@ public class PackageUtils { return -1; } } public static String versionName(Context context, String packageName) { try { return context.getPackageManager().getPackageInfo(packageName, 0).versionName; } catch (PackageManager.NameNotFoundException e) { return null; } } }
play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterHandler.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * 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)); } }
play-services-core/src/main/java/org/microg/gms/gcm/PushRegisterManager.java 0 → 100644 +168 −0 Original line number Diff line number Diff line /* * 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; } }