diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4865660d..c342693be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
# master
+### BREAKING CHANGES
+- Remove `gcm` module since GCM is no longer supported by Google (John Carlson) [#1091](https://github.com/parse-community/Parse-SDK-Android/pull/1091)
+
# 2.0.0-alpha.1
### BREAKING CHANGES
diff --git a/gcm/.gitignore b/gcm/.gitignore
deleted file mode 100644
index 796b96d1c..000000000
--- a/gcm/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/gcm/README.md b/gcm/README.md
deleted file mode 100644
index 825f97307..000000000
--- a/gcm/README.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# Parse SDK Android GCM
-GCM support for Parse Android apps
-
-## Deprecated
-Please note that GCM is deprecated in favor of FCM. This module exists as a backwards compatible solution for projects already using GCM. New apps should instead use FCM. Note that if your app targets Android Q or later, [GCM will not function](https://developers.google.com/cloud-messaging/faq)
-
-## Dependency
-
-After including JitPack:
-```gradle
-dependencies {
- implementation "com.github.parse-community.Parse-SDK-Android:gcm:latest.version.here"
-}
-```
-
-You will then need to register some things in your manifest, firstly, the GCM sender ID:
-```xml
-
-```
-The sender ID should be all numbers. Make sure you are keeping the `id:` in the front.
-
-Next you will register the GcmReceiver:
-```xml
-
-
-
-
-
-
-```
-And to listen for the pushes from GCM:
-```xml
-
-
-
-
-
-```
-And finally, to register the device for GCM pushes:
-```xml
-
-
-
-
-
-```
-
-After these services are registered in the Manifest, you then need to register your push broadcast receiver:
-```xml
-
-
-
-
-
-
-
-```
-
-After this, you are all set. Adding the `parse-gcm-android` package will include a [ParseGCMJobService](https://github.com/parse-community/Parse-SDK-Android/blob/master/gcm/src/main/java/com/parse/gcm/ParseGCMJobService.java) in the `AndroidManifest.xml` file that will register for a GCM token when the app starts. You should see `ParseGCM: GCM registration success` messages assuming you have enabled logging:
-
-```java
-Parse.setLogLevel(Parse.LOG_LEVEL_DEBUG);
-```
-
-## Custom Notifications
-If you need to customize the notification that is sent out from a push, you can do so easily by extending `ParsePushBroadcastReceiver` with your own class and registering it instead in the Manifest.
-
-## License
- Copyright (c) 2015-present, Parse, LLC.
- All rights reserved.
-
- This source code is licensed under the BSD-style license found in the
- LICENSE file in the root directory of this source tree. An additional grant
- of patent rights can be found in the PATENTS file in the same directory.
diff --git a/gcm/build.gradle b/gcm/build.gradle
deleted file mode 100644
index 791e2c902..000000000
--- a/gcm/build.gradle
+++ /dev/null
@@ -1,45 +0,0 @@
-apply plugin: "com.android.library"
-apply plugin: "io.freefair.android-javadoc-jar"
-apply plugin: "io.freefair.android-sources-jar"
-
-android {
- compileSdkVersion rootProject.ext.compileSdkVersion
-
- defaultConfig {
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
- }
-
- packagingOptions {
- exclude "**/BuildConfig.class"
- }
-
- lintOptions {
- abortOnError false
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
- }
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
-}
-
-dependencies {
- // last version for GCM to be supported
- api "com.google.android.gms:play-services-gcm:17.0.0"
- api "com.firebase:firebase-jobdispatcher:0.8.6"
- implementation project(":parse")
-}
\ No newline at end of file
diff --git a/gcm/proguard-rules.pro b/gcm/proguard-rules.pro
deleted file mode 100644
index f1b424510..000000000
--- a/gcm/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/gcm/src/main/AndroidManifest.xml b/gcm/src/main/AndroidManifest.xml
deleted file mode 100644
index 2c9bcc51c..000000000
--- a/gcm/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/gcm/src/main/java/com/parse/gcm/ParseGCM.java b/gcm/src/main/java/com/parse/gcm/ParseGCM.java
deleted file mode 100644
index 7009bc496..000000000
--- a/gcm/src/main/java/com/parse/gcm/ParseGCM.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-package com.parse.gcm;
-
-import android.content.Context;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.firebase.jobdispatcher.FirebaseJobDispatcher;
-import com.firebase.jobdispatcher.GooglePlayDriver;
-import com.firebase.jobdispatcher.Job;
-import com.parse.ManifestInfo;
-import com.parse.PLog;
-
-/**
- * Entry point into setting up Parse GCM Push
- */
-public class ParseGCM {
-
- static final String TAG = "ParseGCM";
-
- private static final String SENDER_ID_EXTRA = "com.parse.push.gcm_sender_id";
-
- /**
- * Register your app to start receiving GCM pushes
- *
- * @param context context
- */
- public static void register(@NonNull Context context) {
- //kicks off the background job
- PLog.d(TAG, "Scheduling job to register Parse GCM");
- FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context.getApplicationContext()));
- Job job = ParseGCMJobService.createJob(dispatcher, gcmSenderFromManifest(context));
- dispatcher.mustSchedule(job);
- }
-
- @Nullable
- private static String gcmSenderFromManifest(Context context) {
- // Look for an element like this as a child of the element:
- //
- //
- //
- // The reason why the "id:" prefix is necessary is because Android treats any metadata value
- // that is a string of digits as an integer. So the call to Bundle.getString() will actually
- // return null for `android:value="567327206255"`. Additionally, Bundle.getInteger() returns
- // a 32-bit integer. For `android:value="567327206255"`, this returns a truncated integer
- // because 567327206255 is larger than the largest 32-bit integer.
- Bundle metaData = ManifestInfo.getApplicationMetadata(context);
- String senderID = null;
-
- if (metaData != null) {
- Object senderIDExtra = metaData.get(SENDER_ID_EXTRA);
-
- if (senderIDExtra != null) {
- senderID = actualSenderIDFromExtra(senderIDExtra);
-
- if (senderID == null) {
- PLog.e(TAG, "Found " + SENDER_ID_EXTRA + " element with value \"" +
- senderIDExtra.toString() + "\", but the value is missing the expected \"id:\" " +
- "prefix.");
- return null;
- }
- }
- }
-
- if (senderID == null) {
- PLog.e(TAG, "You must provide " + SENDER_ID_EXTRA + " in your AndroidManifest.xml\n" +
- "Make sure to prefix with the value with id:\n\n" +
- "\" />");
- return null;
- }
- return senderID;
- }
-
- private static String actualSenderIDFromExtra(Object senderIDExtra) {
- if (!(senderIDExtra instanceof String)) {
- return null;
- }
-
- String senderID = (String) senderIDExtra;
- if (!senderID.startsWith("id:")) {
- return null;
- }
-
- return senderID.substring(3);
- }
-}
diff --git a/gcm/src/main/java/com/parse/gcm/ParseGCMInstanceIDListenerService.java b/gcm/src/main/java/com/parse/gcm/ParseGCMInstanceIDListenerService.java
deleted file mode 100644
index fe371a989..000000000
--- a/gcm/src/main/java/com/parse/gcm/ParseGCMInstanceIDListenerService.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.parse.gcm;
-
-import com.google.android.gms.iid.InstanceIDListenerService;
-
-/**
- * Listens for GCM token refreshes and kicks off a background job to save the token
- */
-public class ParseGCMInstanceIDListenerService extends InstanceIDListenerService {
-
- @Override
- public void onTokenRefresh() {
- super.onTokenRefresh();
- ParseGCM.register(getApplicationContext());
- }
-}
diff --git a/gcm/src/main/java/com/parse/gcm/ParseGCMJobService.java b/gcm/src/main/java/com/parse/gcm/ParseGCMJobService.java
deleted file mode 100644
index 8c5c2e058..000000000
--- a/gcm/src/main/java/com/parse/gcm/ParseGCMJobService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2015-present, Parse, LLC.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-package com.parse.gcm;
-
-import android.os.Bundle;
-
-import com.firebase.jobdispatcher.Constraint;
-import com.firebase.jobdispatcher.FirebaseJobDispatcher;
-import com.firebase.jobdispatcher.Job;
-import com.firebase.jobdispatcher.JobParameters;
-import com.firebase.jobdispatcher.JobService;
-import com.firebase.jobdispatcher.RetryStrategy;
-import com.google.android.gms.gcm.GoogleCloudMessaging;
-import com.google.android.gms.iid.InstanceID;
-import com.parse.PLog;
-import com.parse.ParseInstallation;
-import com.parse.boltsinternal.Task;
-
-import java.util.concurrent.Callable;
-
-/**
- * Handles saving the GCM token to the Parse Installation
- */
-public class ParseGCMJobService extends JobService {
-
- private static final String JOB_TAG_REGISTER = "register";
- private static final String KEY_GCM_SENDER_ID = "gcm_sender_id";
- private static final String PUSH_TYPE = "gcm";
-
- static Job createJob(FirebaseJobDispatcher dispatcher, String gcmSenderId) {
- Bundle extras = new Bundle();
- extras.putString(KEY_GCM_SENDER_ID, gcmSenderId);
- return dispatcher.newJobBuilder()
- .setExtras(extras)
- .setRecurring(false)
- .setReplaceCurrent(true)
- // retry with exponential backoff
- .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
- .setConstraints(
- // only run on a network
- Constraint.ON_ANY_NETWORK
- )
- .setService(ParseGCMJobService.class) // the JobService that will be called
- .setTag(JOB_TAG_REGISTER) // uniquely identifies the job
- .build();
- }
-
- @Override
- public boolean onStartJob(final JobParameters job) {
- PLog.d(ParseGCM.TAG, "Updating GCM token");
-
- Task.callInBackground((Callable) () -> {
- try {
- InstanceID instanceID = InstanceID.getInstance(getApplicationContext());
- String senderId = job.getExtras().getString(KEY_GCM_SENDER_ID);
- String token = instanceID.getToken(senderId,
- GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
- ParseInstallation installation = ParseInstallation.getCurrentInstallation();
- installation.setDeviceToken(token);
- //even though this is FCM, calling it gcm will work on the backend
- installation.setPushType(PUSH_TYPE);
- installation.save();
- PLog.d(ParseGCM.TAG, "GCM registration success");
- } catch (Exception e) {
- PLog.e(ParseGCM.TAG, "GCM registration failed", e);
- jobFinished(job, true);
- }
- return null;
- });
- return true; // Answers the question: "Is there still work going on?"
- }
-
- @Override
- public boolean onStopJob(JobParameters job) {
- return true; // Answers the question: "Should this job be retried?"
- }
-}
diff --git a/gcm/src/main/java/com/parse/gcm/ParseGCMListenerService.java b/gcm/src/main/java/com/parse/gcm/ParseGCMListenerService.java
deleted file mode 100644
index e379a531e..000000000
--- a/gcm/src/main/java/com/parse/gcm/ParseGCMListenerService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.parse.gcm;
-
-import android.os.Bundle;
-
-import com.google.android.gms.gcm.GcmListenerService;
-import com.parse.PLog;
-import com.parse.PushRouter;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class ParseGCMListenerService extends GcmListenerService {
-
- @Override
- public void onMessageReceived(String s, Bundle bundle) {
- super.onMessageReceived(s, bundle);
- String pushId = bundle.getString("push_id");
- String timestamp = bundle.getString("time");
- String dataString = bundle.getString("data");
- String channel = bundle.getString("channel");
-
- JSONObject data = null;
- if (dataString != null) {
- try {
- data = new JSONObject(dataString);
- } catch (JSONException e) {
- PLog.e(ParseGCM.TAG, "Ignoring push because of JSON exception while processing: " + dataString, e);
- return;
- }
- }
-
- PushRouter.getInstance().handlePush(pushId, timestamp, channel, data);
- }
-}
diff --git a/settings.gradle b/settings.gradle
index 93b9b130f..9a8336c06 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
-include ':parse', ':fcm', ':gcm', ':ktx', ':coroutines', 'rxjava', ':google', ':facebook', ':twitter', ':bolts-tasks'
+include ':parse', ':fcm', ':ktx', ':coroutines', 'rxjava', ':google', ':facebook', ':twitter', ':bolts-tasks'