messages_;
};
@@ -221,6 +315,14 @@ void PollableListener::OnTokenReceived(const char* token) {
impl_->OnTokenReceived(token);
}
+void PollableListener::OnRegistrationReceived(const char* installationId) {
+ impl_->OnRegistrationReceived(installationId);
+}
+
+void PollableListener::OnUnregistrationReceived(const char* installationId) {
+ impl_->OnUnregistrationReceived(installationId);
+}
+
bool PollableListener::PollMessage(Message* out_message) {
return impl_->PollMessage(out_message);
}
@@ -231,6 +333,18 @@ std::string PollableListener::PollRegistrationToken(bool* got_token) {
return out_token;
}
+std::string PollableListener::PollRegistration(bool* got_registration) {
+ std::string out_registration;
+ *got_registration = impl_->PollRegistration(&out_registration);
+ return out_registration;
+}
+
+std::string PollableListener::PollUnregistration(bool* got_unregistration) {
+ std::string out_unregistration;
+ *got_unregistration = impl_->PollUnregistration(&out_unregistration);
+ return out_unregistration;
+}
+
FutureData* FutureData::s_future_data_;
// Create FutureData singleton.
diff --git a/messaging/src/common.h b/messaging/src/common.h
index 09453a433f..c3e6b5febe 100644
--- a/messaging/src/common.h
+++ b/messaging/src/common.h
@@ -40,6 +40,8 @@ enum MessagingFn {
kMessagingFnUnsubscribe,
kMessagingFnGetToken,
kMessagingFnDeleteToken,
+ kMessagingFnRegister,
+ kMessagingFnUnregister,
kMessagingFnCount
};
@@ -81,6 +83,12 @@ void NotifyListenerOnMessage(const Message& message);
// Notify the currently set listener of a new token.
void NotifyListenerOnTokenReceived(const char* token);
+// Notify the currently set listener of a new registration installation ID.
+void NotifyListenerOnRegistrationReceived(const char* installationId);
+
+// Notify the currently set listener of an unregistration installation ID.
+void NotifyListenerOnUnregistrationReceived(const char* installationId);
+
} // namespace messaging
} // namespace firebase
diff --git a/messaging/src/include/firebase/messaging.h b/messaging/src/include/firebase/messaging.h
index 3d1535279a..aebeb09cee 100644
--- a/messaging/src/include/firebase/messaging.h
+++ b/messaging/src/include/firebase/messaging.h
@@ -376,7 +376,25 @@ class Listener {
/// firebase::messaging::Initialize(...).
///
/// @param[in] token The registration token.
- virtual void OnTokenReceived(const char* token) = 0;
+ /// @deprecated Use OnRegistrationReceived(const char*) instead.
+ FIREBASE_DEPRECATED virtual void OnTokenReceived(const char* token);
+
+ /// @brief Called on the client when the current app instance has been
+ /// successfully registered with FCM.
+ ///
+ /// This callback provides the unique Firebase Installation ID (FID), which
+ /// should be used to target this app instance for direct-send messaging.
+ ///
+ /// @param[in] installationId The Firebase Installation ID used for sending
+ /// messages to the current app instance.
+ virtual void OnRegistrationReceived(const char* installationId);
+
+ /// @brief Called on the client when the current app instance has been
+ /// successfully unregistered from FCM via a call to Unregister().
+ ///
+ /// @param[in] installationId The Firebase Installation ID of the current app
+ /// instance that was unregistered with FCM.
+ virtual void OnUnregistrationReceived(const char* installationId);
};
/// @brief Initialize Firebase Cloud Messaging.
@@ -417,10 +435,62 @@ InitResult Initialize(const App& app, Listener* listener,
/// @note On Android, the services will not be shut down by this method.
void Terminate();
+/// @brief Determines if automatic registration during initialization is
+/// enabled.
+///
+/// @return true if auto registration is enabled and false if disabled.
+bool IsRegistrationOnInitEnabled();
+
+/// @brief Enable or disable registration during initialization of Firebase
+/// Cloud Messaging.
+///
+/// The installation ID returned is what identifies the user to Firebase, so
+/// disabling this avoids creating any new identity and automatically sending
+/// it to Firebase, unless consent has been granted.
+///
+/// If this setting is enabled, it triggers the registration refresh
+/// immediately. This setting is persisted across app restarts and overrides the
+/// setting "firebase_messaging_auto_init_enabled" specified in your Android
+/// manifest (on Android) or Info.plist (on iOS and tvOS).
+///
+/// By default, registration during initialization is enabled.
+///
+/// The registration happens before you can programmatically disable it, so
+/// if you need to change the default, (for example, because you want to prompt
+/// the user before FCM generates/refreshes a registration on app
+/// startup), add to your application’s manifest:
+///
+/// @if NOT_DOXYGEN
+///
+/// @else
+/// @code
+/// <meta-data android:name="firebase_messaging_auto_init_enabled"
+/// android:value="false" />
+/// @endcode
+/// @endif
+///
+/// or on iOS or tvOS to your Info.plist:
+///
+/// @if NOT_DOXYGEN
+/// FirebaseMessagingAutoInitEnabled
+///
+/// @else
+/// @code
+/// <key>FirebaseMessagingAutoInitEnabled</key>
+/// <false/>
+/// @endcode
+/// @endif
+///
+/// @param enable sets if a registration should be requested on
+/// initialization.
+void SetRegistrationOnInitEnabled(bool enable);
+
/// Determines if automatic token registration during initalization is enabled.
///
/// @return true if auto token registration is enabled and false if disabled.
-bool IsTokenRegistrationOnInitEnabled();
+/// @deprecated Use IsRegistrationOnInitEnabled() instead.
+FIREBASE_DEPRECATED bool IsTokenRegistrationOnInitEnabled();
/// Enable or disable token registration during initialization of Firebase Cloud
/// Messaging.
@@ -466,7 +536,8 @@ bool IsTokenRegistrationOnInitEnabled();
///
/// @param enable sets if a registration token should be requested on
/// initialization.
-void SetTokenRegistrationOnInitEnabled(bool enable);
+/// @deprecated Use SetRegistrationOnInitEnabled(bool) instead.
+FIREBASE_DEPRECATED void SetTokenRegistrationOnInitEnabled(bool enable);
#ifndef SWIG
/// @brief Set the listener for events from the Firebase Cloud Messaging
@@ -573,17 +644,52 @@ bool DeliveryMetricsExportToBigQueryEnabled();
/// delivery metrics to BigQuery.
void SetDeliveryMetricsExportToBigQuery(bool enable);
+/// @brief Registers the current Firebase app instance with the Firebase Cloud
+/// Messaging (FCM) backend to receive messages.
+///
+/// This creates a Firebase Installations ID (FID), if one does not exist, and
+/// sends information about the application and the device where it's running to
+/// the Firebase backend.
+///
+/// Upon completion, `OnRegistrationReceived` will be triggered with the current
+/// FID. Calling this function when already registered will still invoke the
+/// `OnRegistrationReceived` callback with the existing FID.
+///
+/// @return A future that completes when the registration is completed.
+Future Register();
+
+/// @brief Gets the result of the most recent call to Register().
+///
+/// @return Result of the most recent call to Register().
+Future RegisterLastResult();
+
+/// @brief Unregisters the current app instance with FCM.
+///
+/// Note that this does not delete the Firebase Installations ID that may have
+/// been created during registration. See Installations.Delete() for
+/// deleting that.
+///
+/// @return A future that completes when the unregistration is completed.
+Future Unregister();
+
+/// @brief Gets the result of the most recent call to Unregister().
+///
+/// @return Result of the most recent call to Unregister().
+Future UnregisterLastResult();
+
/// @brief This creates a Firebase Installations ID, if one does not exist, and
/// sends information about the application and the device where it's running to
/// the Firebase backend.
///
/// @return A future with the token.
-Future GetToken();
+/// @deprecated Use Register() instead.
+FIREBASE_DEPRECATED Future GetToken();
/// @brief Gets the result of the most recent call to GetToken();
///
/// @return Result of the most recent call to GetToken().
-Future GetTokenLastResult();
+/// @deprecated Use RegisterLastResult() instead.
+FIREBASE_DEPRECATED Future GetTokenLastResult();
/// @brief Deletes the default token for this Firebase project.
///
@@ -592,40 +698,28 @@ Future GetTokenLastResult();
/// deleting that.
///
/// @return A future that completes when the token is deleted.
-Future DeleteToken();
+/// @deprecated Use Unregister() instead.
+FIREBASE_DEPRECATED Future DeleteToken();
/// @brief Gets the result of the most recent call to DeleteToken();
///
/// @return Result of the most recent call to DeleteToken().
-Future DeleteTokenLastResult();
+/// @deprecated Use UnregisterLastResult() instead.
+FIREBASE_DEPRECATED Future DeleteTokenLastResult();
class PollableListenerImpl;
-/// @brief A listener that can be polled to consume pending `Message`s.
+/// @brief A listener that can be polled to consume pending `Message`s and
+/// registration events.
///
/// This class is intended to be used with applications that have a main loop
/// that frequently updates, such as in the case of a game that has a main
/// loop that updates 30 to 60 times a second. Rather than respond to incoming
-/// messages and tokens via the `OnMessage` virtual function, this class will
-/// queue up the message internally in a thread-safe manner so that it can be
-/// consumed with `PollMessage`. For example:
-///
-/// ::firebase::messaging::PollableListener listener;
-/// ::firebase::messaging::Initialize(app, &listener);
-///
-/// while (true) {
-/// std::string token;
-/// if (listener.PollRegistrationToken(&token)) {
-/// LogMessage("Received a registration token");
-/// }
-///
-/// ::firebase::messaging::Message message;
-/// while (listener.PollMessage(&message)) {
-/// LogMessage("Received a new message");
-/// }
-///
-/// // Remainder of application logic...
-/// }
+/// messages and registration events via the `OnMessage`,
+/// `OnRegistrationReceived`, or `OnUnregistrationReceived` virtual functions,
+/// this class will queue up events internally in a thread-safe manner so that
+/// they can be consumed with `PollMessage`, `PollRegistration`, or
+/// `PollUnregistration`.
class PollableListener : public Listener {
public:
/// @brief The default constructor.
@@ -634,13 +728,24 @@ class PollableListener : public Listener {
/// @brief The required virtual destructor.
virtual ~PollableListener();
- /// @brief An implementation of `OnMessage` which adds the incoming messages
+ /// @brief An implementation of `OnMessage` which adds incoming messages
/// to a queue, which can be consumed by calling `PollMessage`.
- virtual void OnMessage(const Message& message);
+ void OnMessage(const Message& message) override;
/// @brief An implementation of `OnTokenReceived` which stores the incoming
/// token so that it can be consumed by calling `PollRegistrationToken`.
- virtual void OnTokenReceived(const char* token);
+ /// @deprecated Use OnRegistrationReceived(const char*) instead.
+ FIREBASE_DEPRECATED void OnTokenReceived(const char* token) override;
+
+ /// @brief An implementation of `OnRegistrationReceived` which stores the
+ /// incoming installation ID so that it can be consumed by calling
+ /// `PollRegistration`.
+ void OnRegistrationReceived(const char* installationId) override;
+
+ /// @brief An implementation of `OnUnregistrationReceived` which stores the
+ /// incoming installation ID so that it can be consumed by calling
+ /// `PollUnregistration`.
+ void OnUnregistrationReceived(const char* installationId) override;
/// @brief Returns the first message queued up, if any.
///
@@ -648,12 +753,7 @@ class PollableListener : public Listener {
/// queue will be popped and used to populate the `message` argument and the
/// function will return `true`. If there are no pending messages, `false` is
/// returned. This function should be called in a loop until all messages have
- /// been consumed, like so:
- ///
- /// ::firebase::messaging::Message message;
- /// while (listener.PollMessage(&message)) {
- /// LogMessage("Received a new message");
- /// }
+ /// been consumed.
///
/// @param[out] message The `Message` struct to be populated. If there were no
/// pending messages, `message` is not modified.
@@ -662,23 +762,8 @@ class PollableListener : public Listener {
bool PollMessage(Message* message);
/// @brief Returns the registration key, if a new one has been received.
- ///
- /// When a new registration token is received, it is cached internally and can
- /// be retrieved by calling `PollRegistrationToken`. The cached registration
- /// token will be used to populate the `token` argument, then the cache will
- /// be cleared and the function will return `true`. If there is no cached
- /// registration token this function retuns `false`.
- ///
- /// std::string token;
- /// if (listener.PollRegistrationToken(&token)) {
- /// LogMessage("Received a registration token");
- /// }
- ///
- /// @param[out] token A string to be populated with the new token if one has
- /// been received. If there were no new token, the string is left unmodified.
- ///
- /// @return Returns `true` if there was a new token, `false` otherwise.
- bool PollRegistrationToken(std::string* token) {
+ /// @deprecated Use PollRegistration(std::string*) instead.
+ FIREBASE_DEPRECATED bool PollRegistrationToken(std::string* token) {
bool got_token;
std::string token_received = PollRegistrationToken(&got_token);
if (got_token) {
@@ -687,8 +772,56 @@ class PollableListener : public Listener {
return got_token;
}
+ /// @brief Returns the registration installation ID, if a new one has been
+ /// received.
+ ///
+ /// When a new registration installation ID is received, it is cached
+ /// internally and can be retrieved by calling `PollRegistration`. The cached
+ /// installation ID will be used to populate the `installation_id` argument,
+ /// then the cache will be cleared and the function will return `true`. If
+ /// there is no cached registration installation ID, this function returns
+ /// `false`.
+ ///
+ /// @param[out] installation_id A string to be populated with the new
+ /// installation ID.
+ /// @return Returns `true` if there was a new installation ID, `false`
+ /// otherwise.
+ bool PollRegistration(std::string* installation_id) {
+ bool got_registration;
+ std::string id_received = PollRegistration(&got_registration);
+ if (got_registration) {
+ *installation_id = id_received;
+ }
+ return got_registration;
+ }
+
+ /// @brief Returns the unregistration installation ID, if an unregistration
+ /// has occurred.
+ ///
+ /// When an unregistration event is received, it is cached internally and can
+ /// be retrieved by calling `PollUnregistration`. The cached unregistration
+ /// installation ID will be used to populate the `installation_id` argument,
+ /// then the cache will be cleared and the function will return `true`. If
+ /// there is no cached unregistration installation ID, this function returns
+ /// `false`.
+ ///
+ /// @param[out] installation_id A string to be populated with the unregistered
+ /// installation ID.
+ /// @return Returns `true` if there was an unregistration event, `false`
+ /// otherwise.
+ bool PollUnregistration(std::string* installation_id) {
+ bool got_unregistration;
+ std::string id_received = PollUnregistration(&got_unregistration);
+ if (got_unregistration) {
+ *installation_id = id_received;
+ }
+ return got_unregistration;
+ }
+
private:
std::string PollRegistrationToken(bool* got_token);
+ std::string PollRegistration(bool* got_registration);
+ std::string PollUnregistration(bool* got_unregistration);
// The implementation of the `PollableListener`.
PollableListenerImpl* impl_;
diff --git a/messaging/src/ios/fake/FIRMessaging.h b/messaging/src/ios/fake/FIRMessaging.h
index 255e2ef320..f3513490f1 100644
--- a/messaging/src/ios/fake/FIRMessaging.h
+++ b/messaging/src/ios/fake/FIRMessaging.h
@@ -252,6 +252,14 @@ NS_SWIFT_NAME(MessagingDelegate)
didReceiveRegistrationToken:(nonnull NSString *)fcmToken
NS_SWIFT_NAME(messaging(_:didReceiveRegistrationToken:));
+- (void)messaging:(nonnull FIRMessaging *)messaging
+ didReceiveRegistration:(nullable NSString *)installationId
+ NS_SWIFT_NAME(messaging(_:didReceiveRegistration:));
+
+- (void)messaging:(nonnull FIRMessaging *)messaging
+ didUnregister:(nullable NSString *)installationId
+ NS_SWIFT_NAME(messaging(_:didUnregister:));
+
@optional
/// This method is called on iOS 10 devices to handle data messages received via FCM through its
/// direct channel (not via APNS). For iOS 9 and below, the FCM data message is delivered via the
@@ -531,4 +539,10 @@ NS_SWIFT_NAME(Messaging)
- (void)deleteTokenWithCompletion:(void (^)(NSError *__nullable error))completion;
+- (void)registerWithCompletion:(nonnull void (^)(NSError *__nullable error))completion
+ NS_SWIFT_NAME(register(completion:));
+
+- (void)unregisterWithCompletion:(nonnull void (^)(NSError *__nullable error))completion
+ NS_SWIFT_NAME(unregister(completion:));
+
@end
diff --git a/messaging/src/ios/fake/FIRMessaging.mm b/messaging/src/ios/fake/FIRMessaging.mm
index c4b4ba16f1..eca4684abc 100644
--- a/messaging/src/ios/fake/FIRMessaging.mm
+++ b/messaging/src/ios/fake/FIRMessaging.mm
@@ -127,6 +127,10 @@ - (void)tokenWithCompletion:(FIRMessagingFCMTokenFetchCompletion)completion
- (void)deleteTokenWithCompletion:(FIRMessagingDeleteFCMTokenCompletion)completion
NS_SWIFT_NAME(deleteFCMToken(completion:)) {}
+- (void)registerWithCompletion:(void (^)(NSError *__nullable error))completion {}
+
+- (void)unregisterWithCompletion:(void (^)(NSError *__nullable error))completion {}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/messaging/src/ios/messaging.mm b/messaging/src/ios/messaging.mm
index c2fd55ebd4..83d02305f8 100644
--- a/messaging/src/ios/messaging.mm
+++ b/messaging/src/ios/messaging.mm
@@ -47,11 +47,14 @@ @interface FIRCppDelegate : NSObject
// messaging:didReceiveRegistrationToken: is called.
@property(nonatomic, readwrite, nullable) FIRMessaging *cachedMessaging;
@property(nonatomic, readwrite, nullable) NSString *cachedFCMToken;
+@property(nonatomic, readwrite, nullable) NSString *cachedRegistrationId;
// If a listener is set, this will notify Messaging of the token as normal.
// If no listener is set yet, the data will be cached until one is.
// NOLINTNEXTLINE
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(nullable NSString *)FCMToken;
+- (void)messaging:(FIRMessaging *)messaging didReceiveRegistration:(nullable NSString *)installationId;
+- (void)messaging:(FIRMessaging *)messaging didUnregister:(nullable NSString *)installationId;
// Once the listener is registered, process cached token (if there is one).
// This will call messaging:didReceiveRegistrationToken:, passing in the cached values:
@@ -281,6 +284,7 @@ void Terminate() {
g_delegate.isListenerSet = NO;
g_delegate.cachedMessaging = nil;
g_delegate.cachedFCMToken = nil;
+ g_delegate.cachedRegistrationId = nil;
}
SetListener(nullptr);
g_app = nullptr;
@@ -793,6 +797,12 @@ void SetDeliveryMetricsExportToBigQuery(bool /*enable*/) {
LogWarning("SetDeliveryMetricsExportToBigQuery is not currently implemented on iOS");
}
+bool IsRegistrationOnInitEnabled() { return IsTokenRegistrationOnInitEnabled(); }
+
+void SetRegistrationOnInitEnabled(bool enable) {
+ SetTokenRegistrationOnInitEnabled(enable);
+}
+
bool IsTokenRegistrationOnInitEnabled() { return [FIRMessaging messaging].autoInitEnabled; }
void SetTokenRegistrationOnInitEnabled(bool enable) {
@@ -807,6 +817,50 @@ void SetTokenRegistrationOnInitEnabled(bool enable) {
}
}
+Future Register() {
+ FIREBASE_ASSERT_RETURN(RegisterLastResult(), internal::IsInitialized());
+
+ ReferenceCountedFutureImpl* api = FutureData::Get()->api();
+ SafeFutureHandle handle = api->SafeAlloc(kMessagingFnRegister);
+
+ [[FIRMessaging messaging] registerWithCompletion:^(NSError *_Nullable error) {
+ api->Complete(handle,
+ error == nullptr ? kErrorNone : kErrorUnknown,
+ util::NSStringToString(error.localizedDescription).c_str());
+ }];
+
+ return MakeFuture(api, handle);
+}
+
+Future RegisterLastResult() {
+ FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized());
+ ReferenceCountedFutureImpl* api = FutureData::Get()->api();
+ return static_cast&>(
+ api->LastResult(kMessagingFnRegister));
+}
+
+Future Unregister() {
+ FIREBASE_ASSERT_RETURN(UnregisterLastResult(), internal::IsInitialized());
+
+ ReferenceCountedFutureImpl* api = FutureData::Get()->api();
+ SafeFutureHandle handle = api->SafeAlloc(kMessagingFnUnregister);
+
+ [[FIRMessaging messaging] unregisterWithCompletion:^(NSError *_Nullable error) {
+ api->Complete(handle,
+ error == nullptr ? kErrorNone : kErrorUnknown,
+ util::NSStringToString(error.localizedDescription).c_str());
+ }];
+
+ return MakeFuture(api, handle);
+}
+
+Future UnregisterLastResult() {
+ FIREBASE_ASSERT_RETURN(Future(), internal::IsInitialized());
+ ReferenceCountedFutureImpl* api = FutureData::Get()->api();
+ return static_cast&>(
+ api->LastResult(kMessagingFnUnregister));
+}
+
Future GetToken() {
FIREBASE_ASSERT_RETURN(GetTokenLastResult(), internal::IsInitialized());
@@ -934,21 +988,49 @@ - (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSStrin
}
}
+- (void)messaging:(FIRMessaging *)messaging didReceiveRegistration:(NSString *)installationId {
+ ::firebase::messaging::g_delegate_mutex.Acquire();
+ if (_isListenerSet) {
+ ::firebase::messaging::g_delegate_mutex.Release();
+ ::firebase::LogInfo("FCM: registration installation ID received.");
+ ::firebase::messaging::NotifyListenerOnRegistrationReceived(installationId.UTF8String);
+ } else {
+ _cachedMessaging = messaging;
+ _cachedRegistrationId = installationId;
+ ::firebase::messaging::g_delegate_mutex.Release();
+ ::firebase::LogInfo(
+ "FCM: registration installation ID received, but no listener set yet - cached the ID.");
+ }
+}
+
+- (void)messaging:(FIRMessaging *)messaging didUnregister:(NSString *)installationId {
+ ::firebase::LogInfo("FCM: unregistration received.");
+ ::firebase::messaging::NotifyListenerOnUnregistrationReceived(installationId.UTF8String);
+}
+
- (void)processCachedRegistrationToken {
FIRMessaging *msg;
NSString *token;
+ NSString *regId;
{
firebase::MutexLock lock(::firebase::messaging::g_delegate_mutex);
// TODO(butterfield): What if there's no cached message but there is a cached token?
// The user may never get notified of the old token before a new one is registered.
- if (!_isListenerSet || !_cachedMessaging || !_cachedFCMToken) {
+ if (!_isListenerSet) {
return;
}
msg = _cachedMessaging;
token = _cachedFCMToken;
+ regId = _cachedRegistrationId;
_cachedFCMToken = nil;
+ _cachedRegistrationId = nil;
_cachedMessaging = nil;
}
- [self messaging:msg didReceiveRegistrationToken:token];
+ if (token) {
+ [self messaging:msg didReceiveRegistrationToken:token];
+ }
+ if (regId) {
+ [self messaging:msg didReceiveRegistration:regId];
+ }
}
@end
diff --git a/messaging/src/listener.cc b/messaging/src/listener.cc
index f0587b84eb..7eba18dda9 100644
--- a/messaging/src/listener.cc
+++ b/messaging/src/listener.cc
@@ -21,5 +21,11 @@ namespace messaging {
// to prevent its vtable being emitted in each translation unit.
Listener::~Listener() {}
+void Listener::OnTokenReceived(const char* /*token*/) {}
+
+void Listener::OnRegistrationReceived(const char* /*installationId*/) {}
+
+void Listener::OnUnregistrationReceived(const char* /*installationId*/) {}
+
} // namespace messaging
} // namespace firebase
diff --git a/messaging/src/stub/messaging.cc b/messaging/src/stub/messaging.cc
index 20b5a3a9a2..c07b69a4f8 100644
--- a/messaging/src/stub/messaging.cc
+++ b/messaging/src/stub/messaging.cc
@@ -190,6 +190,10 @@ Future RequestPermissionLastResult() {
return GetLastResultFuture(kMessagingFnRequestPermission);
}
+bool IsRegistrationOnInitEnabled() { return true; }
+
+void SetRegistrationOnInitEnabled(bool /*enable*/) {}
+
bool IsTokenRegistrationOnInitEnabled() { return true; }
void SetTokenRegistrationOnInitEnabled(bool /*enable*/) {}
@@ -198,6 +202,24 @@ bool DeliveryMetricsExportToBigQueryEnabled() { return false; }
void SetDeliveryMetricsExportToBigQuery(bool /*enable*/) {}
+Future Register() {
+ NotifyListenerOnRegistrationReceived("StubRegistrationId");
+ return CreateAndCompleteStubFuture(kMessagingFnRegister);
+}
+
+Future RegisterLastResult() {
+ return GetLastResultFuture(kMessagingFnRegister);
+}
+
+Future Unregister() {
+ NotifyListenerOnUnregistrationReceived("StubRegistrationId");
+ return CreateAndCompleteStubFuture(kMessagingFnUnregister);
+}
+
+Future UnregisterLastResult() {
+ return GetLastResultFuture(kMessagingFnUnregister);
+}
+
Future GetToken() {
ReferenceCountedFutureImpl* api = FutureData::Get()->api();
SafeFutureHandle handle =
diff --git a/messaging/tests/CMakeLists.txt b/messaging/tests/CMakeLists.txt
index afad30765d..dd3ed3a3b6 100644
--- a/messaging/tests/CMakeLists.txt
+++ b/messaging/tests/CMakeLists.txt
@@ -16,7 +16,7 @@ if(ANDROID OR IOS)
set(messaging_test_util_common_SRCS
messaging_test_util.h)
set(messaging_test_util_android_SRCS
- android/messaging_test_util.cc)
+ android/cpp/messaging_test_util.cc)
set(messaging_test_util_ios_SRCS
ios/messaging_test_util.mm)
diff --git a/messaging/tests/android/cpp/message_reader_test.cc b/messaging/tests/android/cpp/message_reader_test.cc
index aa3d1a2f17..57bbb9bf71 100644
--- a/messaging/tests/android/cpp/message_reader_test.cc
+++ b/messaging/tests/android/cpp/message_reader_test.cc
@@ -39,15 +39,23 @@ using com::google::firebase::messaging::cpp::CreateDataPairDirect;
using com::google::firebase::messaging::cpp::CreateSerializedEvent;
using com::google::firebase::messaging::cpp::CreateSerializedMessageDirect;
using com::google::firebase::messaging::cpp::CreateSerializedNotificationDirect;
+using com::google::firebase::messaging::cpp::
+ CreateSerializedRegistrationReceived;
using com::google::firebase::messaging::cpp::CreateSerializedTokenReceived;
+using com::google::firebase::messaging::cpp::
+ CreateSerializedUnregistrationReceived;
using com::google::firebase::messaging::cpp::DataPair;
using com::google::firebase::messaging::cpp::FinishSerializedEventBuffer;
using com::google::firebase::messaging::cpp::SerializedEventUnion;
using com::google::firebase::messaging::cpp::SerializedEventUnion_MAX;
using com::google::firebase::messaging::cpp::
SerializedEventUnion_SerializedMessage;
+using com::google::firebase::messaging::cpp::
+ SerializedEventUnion_SerializedRegistrationReceived;
using com::google::firebase::messaging::cpp::
SerializedEventUnion_SerializedTokenReceived;
+using com::google::firebase::messaging::cpp::
+ SerializedEventUnion_SerializedUnregistrationReceived;
using flatbuffers::FlatBufferBuilder;
class MessageReaderTest : public ::testing::Test {
@@ -57,6 +65,8 @@ class MessageReaderTest : public ::testing::Test {
void TearDown() override {
messages_received_.clear();
tokens_received_.clear();
+ registrations_received_.clear();
+ unregistrations_received_.clear();
}
// Stores the message in this class.
@@ -73,11 +83,31 @@ class MessageReaderTest : public ::testing::Test {
test->tokens_received_.push_back(std::string(token));
}
+ // Stores the registration installation ID in this class.
+ static void RegistrationReceived(const char* installationId,
+ void* callback_data) {
+ MessageReaderTest* test =
+ reinterpret_cast(callback_data);
+ test->registrations_received_.push_back(std::string(installationId));
+ }
+
+ // Stores the unregistration installation ID in this class.
+ static void UnregistrationReceived(const char* installationId,
+ void* callback_data) {
+ MessageReaderTest* test =
+ reinterpret_cast(callback_data);
+ test->unregistrations_received_.push_back(std::string(installationId));
+ }
+
protected:
// Messages received by MessageReceived().
std::vector messages_received_;
// Tokens received by TokenReceived().
std::vector tokens_received_;
+ // Registrations received by RegistrationReceived().
+ std::vector registrations_received_;
+ // Unregistrations received by UnregistrationReceived().
+ std::vector unregistrations_received_;
};
TEST_F(MessageReaderTest, Construct) {
@@ -279,10 +309,70 @@ TEST_F(MessageReaderTest, ReadFromBufferInvalidEventType) {
AppendFlatBufferToString(&buffer, fbb);
MessageReader reader(MessageReaderTest::MessageReceived, this,
- MessageReaderTest::TokenReceived, this);
+ MessageReaderTest::TokenReceived, this,
+ MessageReaderTest::RegistrationReceived, this,
+ MessageReaderTest::UnregistrationReceived, this);
+ reader.ReadFromBuffer(buffer);
+ EXPECT_EQ(0, messages_received_.size());
+ EXPECT_EQ(0, tokens_received_.size());
+ EXPECT_EQ(0, registrations_received_.size());
+ EXPECT_EQ(0, unregistrations_received_.size());
+}
+
+// Read registration events from a buffer.
+TEST_F(MessageReaderTest, ReadFromBufferRegistrationReceived) {
+ std::string buffer;
+ std::string reg_ids[2] = {"install_id_1", "install_id_2"};
+ for (size_t i = 0; i < 2; ++i) {
+ FlatBufferBuilder fbb;
+ FinishSerializedEventBuffer(
+ fbb, CreateSerializedEvent(
+ fbb, SerializedEventUnion_SerializedRegistrationReceived,
+ CreateSerializedRegistrationReceived(
+ fbb, fbb.CreateString(reg_ids[i]))
+ .Union()));
+ AppendFlatBufferToString(&buffer, fbb);
+ }
+
+ MessageReader reader(MessageReaderTest::MessageReceived, this,
+ MessageReaderTest::TokenReceived, this,
+ MessageReaderTest::RegistrationReceived, this,
+ MessageReaderTest::UnregistrationReceived, this);
+ reader.ReadFromBuffer(buffer);
+ EXPECT_EQ(0, messages_received_.size());
+ EXPECT_EQ(0, tokens_received_.size());
+ EXPECT_EQ(2, registrations_received_.size());
+ EXPECT_EQ(reg_ids[0], registrations_received_[0]);
+ EXPECT_EQ(reg_ids[1], registrations_received_[1]);
+ EXPECT_EQ(0, unregistrations_received_.size());
+}
+
+// Read unregistration events from a buffer.
+TEST_F(MessageReaderTest, ReadFromBufferUnregistrationReceived) {
+ std::string buffer;
+ std::string unreg_ids[2] = {"unreg_id_1", "unreg_id_2"};
+ for (size_t i = 0; i < 2; ++i) {
+ FlatBufferBuilder fbb;
+ FinishSerializedEventBuffer(
+ fbb, CreateSerializedEvent(
+ fbb, SerializedEventUnion_SerializedUnregistrationReceived,
+ CreateSerializedUnregistrationReceived(
+ fbb, fbb.CreateString(unreg_ids[i]))
+ .Union()));
+ AppendFlatBufferToString(&buffer, fbb);
+ }
+
+ MessageReader reader(MessageReaderTest::MessageReceived, this,
+ MessageReaderTest::TokenReceived, this,
+ MessageReaderTest::RegistrationReceived, this,
+ MessageReaderTest::UnregistrationReceived, this);
reader.ReadFromBuffer(buffer);
EXPECT_EQ(0, messages_received_.size());
EXPECT_EQ(0, tokens_received_.size());
+ EXPECT_EQ(0, registrations_received_.size());
+ EXPECT_EQ(2, unregistrations_received_.size());
+ EXPECT_EQ(unreg_ids[0], unregistrations_received_[0]);
+ EXPECT_EQ(unreg_ids[1], unregistrations_received_[1]);
}
} // namespace internal
diff --git a/messaging/tests/android/cpp/messaging_test_util.cc b/messaging/tests/android/cpp/messaging_test_util.cc
index 1e71f1ca89..e6e8fd3e4b 100644
--- a/messaging/tests/android/cpp/messaging_test_util.cc
+++ b/messaging/tests/android/cpp/messaging_test_util.cc
@@ -29,13 +29,21 @@
using ::com::google::firebase::messaging::cpp::CreateDataPair;
using ::com::google::firebase::messaging::cpp::CreateSerializedEvent;
+using ::com::google::firebase::messaging::cpp::
+ CreateSerializedRegistrationReceived;
using ::com::google::firebase::messaging::cpp::CreateSerializedTokenReceived;
+using ::com::google::firebase::messaging::cpp::
+ CreateSerializedUnregistrationReceived;
using ::com::google::firebase::messaging::cpp::DataPair;
using ::com::google::firebase::messaging::cpp::SerializedEventUnion;
using ::com::google::firebase::messaging::cpp::
SerializedEventUnion_SerializedMessage;
+using ::com::google::firebase::messaging::cpp::
+ SerializedEventUnion_SerializedRegistrationReceived;
using ::com::google::firebase::messaging::cpp::
SerializedEventUnion_SerializedTokenReceived;
+using ::com::google::firebase::messaging::cpp::
+ SerializedEventUnion_SerializedUnregistrationReceived;
using ::com::google::firebase::messaging::cpp::SerializedMessageBuilder;
using ::com::google::firebase::messaging::cpp::SerializedNotification;
using ::com::google::firebase::messaging::cpp::SerializedNotificationBuilder;
@@ -96,6 +104,28 @@ void OnTokenReceived(const char* tokenstr) {
WriteBuffer(builder);
}
+void OnRegistrationReceived(const char* registration_id) {
+ flatbuffers::FlatBufferBuilder builder;
+ auto reg = builder.CreateString(registration_id ? registration_id : "");
+ auto regreceived = CreateSerializedRegistrationReceived(builder, reg);
+ auto event = CreateSerializedEvent(
+ builder, SerializedEventUnion_SerializedRegistrationReceived,
+ regreceived.Union());
+ builder.Finish(event);
+ WriteBuffer(builder);
+}
+
+void OnUnregistrationReceived(const char* registration_id) {
+ flatbuffers::FlatBufferBuilder builder;
+ auto unreg = builder.CreateString(registration_id ? registration_id : "");
+ auto unregreceived = CreateSerializedUnregistrationReceived(builder, unreg);
+ auto event = CreateSerializedEvent(
+ builder, SerializedEventUnion_SerializedUnregistrationReceived,
+ unregreceived.Union());
+ builder.Finish(event);
+ WriteBuffer(builder);
+}
+
void OnDeletedMessages() {
::flatbuffers::FlatBufferBuilder builder;
auto from = builder.CreateString("");
diff --git a/messaging/tests/ios/messaging_test_util.mm b/messaging/tests/ios/messaging_test_util.mm
index 9e4004b5d8..1e67a4cc98 100644
--- a/messaging/tests/ios/messaging_test_util.mm
+++ b/messaging/tests/ios/messaging_test_util.mm
@@ -54,6 +54,16 @@ void OnTokenReceived(const char *tokenstr) {
didReceiveRegistrationToken:@(tokenstr)];
}
+void OnRegistrationReceived(const char *registration_id) {
+ [[FIRMessaging messaging].delegate messaging:[FIRMessaging messaging]
+ didReceiveRegistration:registration_id ? @(registration_id) : nil];
+}
+
+void OnUnregistrationReceived(const char *registration_id) {
+ [[FIRMessaging messaging].delegate messaging:[FIRMessaging messaging]
+ didUnregister:registration_id ? @(registration_id) : nil];
+}
+
void SleepMessagingTest(double seconds) {
// We want the main loop to process messages while we wait.
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:seconds]];
diff --git a/messaging/tests/messaging_test.cc b/messaging/tests/messaging_test.cc
index 7544750511..0e2117a2ee 100644
--- a/messaging/tests/messaging_test.cc
+++ b/messaging/tests/messaging_test.cc
@@ -48,20 +48,38 @@ class MessagingTestListener : public Listener {
public:
void OnMessage(const Message& message) override;
void OnTokenReceived(const char* token) override;
+ void OnRegistrationReceived(const char* registration_id) override;
+ void OnUnregistrationReceived(const char* registration_id) override;
const Message& GetMessage() const { return message_; }
const std::string& GetToken() const { return token_; }
+ const std::string& GetRegistrationId() const { return registration_id_; }
+
+ const std::string& GetUnregistrationId() const { return unregistration_id_; }
+
int GetOnTokenReceivedCount() const { return on_token_received_count_; }
int GetOnMessageReceivedCount() const { return on_message_received_count_; }
+ int GetOnRegistrationReceivedCount() const {
+ return on_registration_received_count_;
+ }
+
+ int GetOnUnregistrationReceivedCount() const {
+ return on_unregistration_received_count_;
+ }
+
private:
Message message_;
std::string token_;
+ std::string registration_id_;
+ std::string unregistration_id_;
int on_token_received_count_ = 0;
int on_message_received_count_ = 0;
+ int on_registration_received_count_ = 0;
+ int on_unregistration_received_count_ = 0;
};
class MessagingTest : public ::testing::Test {
@@ -113,6 +131,18 @@ void MessagingTestListener::OnTokenReceived(const char* token) {
on_token_received_count_++;
}
+void MessagingTestListener::OnRegistrationReceived(
+ const char* registration_id) {
+ registration_id_ = registration_id;
+ on_registration_received_count_++;
+}
+
+void MessagingTestListener::OnUnregistrationReceived(
+ const char* registration_id) {
+ unregistration_id_ = registration_id;
+ on_unregistration_received_count_++;
+}
+
// Tests only run on Android for now.
TEST_F(MessagingTest, TestInitializeTwice) {
MessagingTestListener listener;
@@ -369,6 +399,83 @@ TEST_F(MessagingTest, TestDeleteToken) {
AddExpectationAndroid("FirebaseMessaging.deleteToken", {});
}
+TEST_F(MessagingTest, TestRegister) {
+ Future result = Register();
+ SleepMessagingTest(1);
+ EXPECT_EQ(result.status(), kFutureStatusComplete);
+ EXPECT_EQ(result.error(), 0);
+ AddExpectationAndroid("FirebaseMessaging.register", {});
+}
+
+TEST_F(MessagingTest, TestUnregister) {
+ Future result = Unregister();
+ SleepMessagingTest(1);
+ EXPECT_EQ(result.status(), kFutureStatusComplete);
+ EXPECT_EQ(result.error(), 0);
+ AddExpectationAndroid("FirebaseMessaging.unregister", {});
+}
+
+TEST_F(MessagingTest, TestRegistrationOnInitEnabled) {
+ EXPECT_TRUE(IsRegistrationOnInitEnabled());
+ SetRegistrationOnInitEnabled(false);
+ EXPECT_FALSE(IsRegistrationOnInitEnabled());
+ SetRegistrationOnInitEnabled(true);
+ EXPECT_TRUE(IsRegistrationOnInitEnabled());
+}
+
+TEST_F(MessagingTest, TestRegistrationReceived) {
+ OnRegistrationReceived("my_installation_id");
+ SleepMessagingTest(1);
+ EXPECT_THAT(listener_.GetRegistrationId(), StrEq("my_installation_id"));
+ EXPECT_EQ(listener_.GetOnRegistrationReceivedCount(), 1);
+}
+
+TEST_F(MessagingTest, TestNullRegistrationReceived) {
+ OnRegistrationReceived(nullptr);
+ OnRegistrationReceived("");
+ SleepMessagingTest(1);
+ EXPECT_EQ(listener_.GetOnRegistrationReceivedCount(), 0);
+}
+
+TEST_F(MessagingTest, TestUnregistrationReceived) {
+ OnUnregistrationReceived("my_installation_id");
+ SleepMessagingTest(1);
+ EXPECT_THAT(listener_.GetUnregistrationId(), StrEq("my_installation_id"));
+ EXPECT_EQ(listener_.GetOnUnregistrationReceivedCount(), 1);
+}
+
+TEST_F(MessagingTest, TestNullUnregistrationReceived) {
+ OnUnregistrationReceived(nullptr);
+ OnUnregistrationReceived("");
+ SleepMessagingTest(1);
+ EXPECT_EQ(listener_.GetOnUnregistrationReceivedCount(), 0);
+}
+
+TEST_F(MessagingTest, TestRegistrationReceivedBeforeInitialize) {
+ Terminate();
+ OnRegistrationReceived("my_installation_id");
+ EXPECT_EQ(Initialize(*firebase_app_, &listener_), kInitResultSuccess);
+ SleepMessagingTest(1);
+ EXPECT_THAT(listener_.GetRegistrationId(), StrEq("my_installation_id"));
+}
+
+TEST_F(MessagingTest, TestTwoRegistrationsReceivedBeforeInitialize) {
+ Terminate();
+ OnRegistrationReceived("my_installation_id1");
+ OnRegistrationReceived("my_installation_id2");
+ EXPECT_EQ(Initialize(*firebase_app_, &listener_), kInitResultSuccess);
+ SleepMessagingTest(1);
+ EXPECT_THAT(listener_.GetRegistrationId(), StrEq("my_installation_id2"));
+}
+
+TEST_F(MessagingTest, TestTwoRegistrationsReceivedAfterInitialize) {
+ OnRegistrationReceived("my_installation_id1");
+ OnRegistrationReceived("my_installation_id2");
+ SleepMessagingTest(1);
+ EXPECT_THAT(listener_.GetRegistrationId(), StrEq("my_installation_id2"));
+ EXPECT_EQ(listener_.GetOnRegistrationReceivedCount(), 2);
+}
+
#endif // defined(FIREBASE_ANDROID_FOR_DESKTOP)
} // namespace messaging
diff --git a/messaging/tests/messaging_test_util.h b/messaging/tests/messaging_test_util.h
index 78ee6f6fee..39f0074861 100644
--- a/messaging/tests/messaging_test_util.h
+++ b/messaging/tests/messaging_test_util.h
@@ -37,6 +37,12 @@ void TerminateMessagingTest();
// Simulate a token received/refresh event from the OS-level implementation.
void OnTokenReceived(const char* tokenstr);
+// Simulate a registration event from the OS-level implementation.
+void OnRegistrationReceived(const char* registration_id);
+
+// Simulate an unregistration event from the OS-level implementation.
+void OnUnregistrationReceived(const char* registration_id);
+
void OnDeletedMessages();
void OnMessageReceived(const Message& message);
diff --git a/release_build_files/readme.md b/release_build_files/readme.md
index 22bbebc02e..b41d9c6efc 100644
--- a/release_build_files/readme.md
+++ b/release_build_files/readme.md
@@ -616,6 +616,8 @@ code.
### Upcoming
- Changes
- Realtime Database (Desktop): Fixed an intermittent use-after-free crash (`ACCESS_VIOLATION`) when detaching a listener while a WebSocket listen response is pending.
+ - Messaging: Added new Registration methods using Installation Ids.
+ Deprecated old Token based methods.
### 13.10.0
- Changes