Repository: firebase/firebase-admin-java
Pull request #320 (2019-10-17 02:56:42)

feat(fcm): Add 12 new android notification params support
 Add 12 new android notification params support
### Testing

  * Added unit tests to reflect this change.

### API Changes

  * In Firebase Cloud Messaging, added new fields in AndroidNotification class.

RELEASE NOTE: Added a series of new parameters to the `AndroidNotification` class that allow further customization of notifications that target Android devices.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index ddc7afb..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,512 +0,0 @@
-# Unreleased
-
--
-
-# v6.10.0
-
-- [added] Added `WebpushFcmOptions` to the `FirebaseMessaging` API, providing 
-  the `setLink()` method.
-
-# v6.9.0
-
-- [added] Added `FcmOptions`, `ApnsFcmOptions` and `AndroidFcmOptions` to the
-  `FirebaseMessaging` API, which all provides a `setAnalyticsLabel()` method.
-- [changed] Upgraded the Cloud Firestore client to 1.9.0.
-- [changed] Upgraded the Cloud Storage client to 1.79.0.
-- [changed] Upgraded the Google API client to 1.30.1.
-
-# v6.8.1
-
-- [fixed] Enabled automatic retries for FCM API calls failing with
-  HTTP 500 and 503 errors.
-
-# v6.8.0
-
-- [added] Implemented new `sendAll()` and `sendMulticast()` APIs in
-  `FirebaseMessaging`.
-- [changed] Removed org.json dependency and replaced with com.google.code.gson.
-- [changed] Upgraded Mockito dependency, and fixed the build on Java 11.
-
-# v6.7.0
-
-- [added] Added `generatePasswordResetLink()`, `generateEmailVerificationLink()`
-  and `generateSignInWithEmailLink()` methods to the `FirebaseAuth` API.
-- `Aps` class now supports configuring a critical sound. A new
-  `CriticalSound` class has been introduced for this purpose.
-- [fixed] `Firestore` instances initialized by the SDK are now cleaned
-  up, when `FirebaseApp.delete()` is called.
-- [added] Added new `setChannelId()` method to the
-  `AndroidNotification.Builder` API for setting the Android
-  notification channel ID (new in Android O).
-
-# v6.6.0
-
-- [added] Added a new `FirebaseProjectManagement` API for managing
-  apps in a Firebase project.
-- [fixed] Fixing error handling in FCM. The SDK now checks the key
-  `type.googleapis.com/google.firebase.fcm.v1.FcmError` to set error
-  code.
-- [fixed] FCM errors sent by the back-end now include more details
-  that are helpful when debugging problems.
-- [changed] Migrated the `FirebaseAuth` user management API to the
-
-# v6.5.0
-
-- [added] `FirebaseOptions.Builder` class now provides a
-  `setFirestoreOptions()` method for configuring the Firestore client.
-- [changed] Upgraded the Cloud Firestore client to 0.61.0-beta.
-- [changed] Upgraded the Cloud Storage client to 1.43.0.
-
-# v6.4.0
-
-- [added] `WebpushNotification` type now supports arbitrary key-value
-  pairs in its payload.
-
-# v6.3.0
-
-- [added] Implemented the ability to create custom tokens without
-  service account credentials.
-- [added] Added the `setServiceAccount()` method to the
-  `FirebaseOptions.Builder` API.
-- [added] The SDK can now read the Firebase/GCP project ID from both
-  `GCLOUD_PROJECT` and `GOOGLE_CLOUD_PROJECT` environment variables.
-
-# v6.2.0
-
-- [added] Added new `importUsersAsync()` API for bulk importing users
-  into Firebase Auth.
-
-# v6.1.0
-
-- [changed] Deprecated the `FirebaseAuth.setCustomClaims()` method.
-  Developers should use the `FirebaseAuth.setCustomUserClaims()` method
-  instead.
-
-# v6.0.0
-
-- [added] `FirebaseAuth`, `FirebaseMessaging` and `FirebaseInstanceId`
-  interfaces now expose a set of blocking methods. Each operation has
-  blocking an asynchronous versions.
-- [changed] Removed the deprecated `FirebaseCredential` interface.
-- [changed] Removed the deprecated `Task` interface along with the
-  `com.google.firebase.tasks` package.
-- [changed] Dropped support for App Engine's Java 7 runtime. Developers
-  are advised to use the Admin SDK with Java 8 when deploying to App
-  Engine.
-- [changed] Removed the deprecated `FirebaseDatabase.setLogLevel()` API
-  and the related logging utilities. Developers should use SLF4J to
-  configure logging directly.
-
-# v5.11.0
-
-- [added] A new `FirebaseAuth.createSessionCookieAsync()` method for
-  creating a long-lived session cookie given a valid ID token.
-- [added] A new `FirebaseAuth.verifySessionCookieAsync()` method for
-  verifying a given cookie string is valid.
-- [fixed] Upgraded Cloud Firestore dependency version to 0.45.0-beta.
-- [fixed] Upgraded Cloud Storage dependency version to 1.27.0.
-- [fixed] Upgraded Netty dependency version to 4.1.22.
-
-# v5.10.0
-
-- [fixed] Using the `HttpTransport` specified at `FirebaseOptions` in
-  `GooglePublicKeysManager`. This enables developers to use a custom
-  transport to fetch public keys when verifying ID tokens and session
-  cookies.
-- [added] Connection timeout and read timeout for HTTP/REST connections
-  can now be configured via `FirebaseOptions.Builder` at app
-  initialization.
-- [added] Added new `setMutableContent()`, `putCustomData()` and
-  `putAllCustomData()` methods to the `Aps.Builder` API.
-- [fixed] Improved error handling in FCM by mapping more server-side
-  errors to client-side error codes.
-
-# v5.9.0
-
-### Cloud Messaging
-
-- [feature] Added the `FirebaseCloudMessaging` API for sending
-  Firebase notifications and managing topic subscriptions.
-
-### Authentication
-
-- [added] The [`verifyIdTokenAsync()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#verifyIdTokenAsync)
-  method has an overload that accepts a boolean `checkRevoked` parameter.
-  When `true`, an additional check is performed to see whether the token
-  has been revoked.
-- [added] A new [`revokeRefreshTokensAsync()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#revokeRefreshTokens)
-  method has been added to invalidate all tokens issued to a user.
-- [added] A new getter `getTokensValidAfterTimestamp()` has been added
-  to the [`UserRecord`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserRecord)
-  class, which denotes the time before which tokens are not valid.
-
-### Realtime Database
-
-- [fixed] Exceptions thrown by database event handlers are now logged.
-
-### Initialization
-
-- [fixed] The [`FirebaseOptions.Builder.setStorageBucket()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder.html#setStorageBucket(java.lang.String))
-  method now throws a clear exception when invoked with a bucket URL
-  instead of the name.
-- [fixed] Implemented a fix for a potential Guava version conflict which
-  was causing an `IllegalStateException` (precondition failure) in some
-  environments.
-
-### Cloud Firestore
-
-- [fixed] Upgraded the Cloud Firestore client to the latest available
-  version.
-
-# v5.8.0
-
-### Initialization
-
-- [added] The [`FirebaseApp.initializeApp()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseApp.html#initializeApp())
-  method now provides an overload that does not require any arguments. This
-  initializes an app using Google Application Default Credentials, and other
-  [`FirebaseOptions`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions)
-  loaded from the `FIREBASE_CONFIG` environment variable.
-
-### Authentication
-
-- [changed] Improved error handling in user management APIs in the
-  [`FirebaseAuth`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth)
-  class. These operations now throw exceptions with well-defined
-  [error codes](https://firebase.google.com/docs/auth/admin/errors).
-
-### Realtime Database
-
-- [changed] The SDK now serializes large whole double values as longs when
-  appropriate.
-
-# v5.7.0
-
-- [added] A new [`FirebaseInstanceId`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/iid/FirebaseInstanceId)
-  API that facilitates deleting instance IDs and associated user data from
-  Firebase projects.
-
-### Authentication
-- [changed] No longer using `org.json` dependency in Authentication APIs, which
-  makes it easier to use the API in environments with conflicting JSON
-  libraries.
-
-# v5.6.0
-
-- [changed] Upgraded the version of Google API Common dependency to the latest
-  (1.2.0).
-
-### Authentication
-- [added] Added the
-  [`listUsersAsync()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#listUsersAsync(java.lang.String))
-  method to the
-  [`FirebaseAuth`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth)
-  class. This method enables listing or iterating over all user accounts
-  in a Firebase project.
-- [added] Added the
-  [`setCustomUserClaimsAsync()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#setCustomUserClaimsAsync(java.lang.String, java.util.Map<java.lang.String, java.lang.Object>))
-  method to the
-  [`FirebaseAuth`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth)
-  class. This method enables setting custom claims on a Firebase user.
-  The custom claims can be accessed via that user's ID token.
-
-### Realtime Database
-- [changed] Re-implemented the WebSocket communication layer of the
-  Realtime Database client using Netty.
-
-# v5.5.0
-
-- [added] A new [`FirestoreClient`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/cloud/FirestoreClient)
-  API that enables access to [Cloud Firestore](https://firebase.google.com/docs/firestore) databases.
-
-### Realtime Database
-- [changed] Ensured graceful termination of database worker threads upon
-  callng [`FirebaseApp.delete()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseApp.html#delete()).
-
-# v5.4.0
-
-- [added] A new [`ThreadManager`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/ThreadManager)
-  API that can be used to specify the thread
-  pool and the `ThreadFactory` that should be used by the SDK.
-- [added] All APIs that support asynchronous operations now return an
-  [`ApiFuture`](https://googleapis.github.io/api-common-java/1.1.0/apidocs/com/google/api/core/ApiFuture.html).
-  The old [`Task`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/tasks/Task)
-  API has been deprecated. For each method `x()`
-  that returns a `Task`, a new `xAsync()` method that returns an `ApiFuture`
-  has been introduced.
-- [changed] The SDK now guarantees the graceful termination of all started
-  threads. In most environments, the SDK will use daemons for all background
-  activities. The developer can also initiate a graceful termination of threads
-  by calling [`FirebaseApp.delete()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseApp.html#delete()).
-
-### Initialization
-
-- [added] [`FirebaseOptions`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions)
-  can now be initialized with
-  [`GoogleCredentials`](http://google.github.io/google-auth-library-java/releases/0.7.1/apidocs/com/google/auth/oauth2/GoogleCredentials.html).
-  This is the new recommended way to specify credentials when initializing the
-  SDK. The old [`FirebaseCredential`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredential)
-  and [`FirebaseCredentials`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials)
-  APIs have been deprecated.
-
-# v5.3.1
-
-### Authentication
-- [changed] Throwing an accurate and more detailed error from
-  [`verifyIdToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#verifyIdToken(java.lang.String))
-  in the event of a low-level exception.
-
-### Realtime Database
-- [changed] Proper handling and logging of exceptions thrown by the
-  [`onComplete()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/database/Transaction.Handler.html#onComplete(com.google.firebase.database.DatabaseError, boolean, com.google.firebase.database.DataSnapshot))
-  event of transaction handlers.
-
-# v5.3.0
-- [added] A new [{{firebase_storage}} API](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/cloud/StorageClient)
-  that facilitates accessing Google Cloud Storage buckets using the
-  [`google-cloud-storage`](http://googlecloudplatform.github.io/google-cloud-java/latest/apidocs/com/google/cloud/storage/Storage.html)
-  library.
-- [added] Integrated with the [SLF4J](https://www.slf4j.org/) library for all logging
-  purposes.
-
-### Authentication
-- [added] Added the method
-  [`getUserByPhoneNumber()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#getUserByPhoneNumber(java.lang.String))
-  to the [`FirebaseAuth`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth)
-  interface. This method
-  enables retrieving user profile information by a phone number.
-- [added] [`CreateRequest`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserRecord.CreateRequest)
-  and [`UpdateRequest`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserRecord.UpdateRequest) types
-  now provide setters for specifying a phone number, which can be used to create users with a phone
-  number field and/or update the phone number associated with a user.
-- [added] Added the `getPhoneNumber()` method to
-  [`UserRecord`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserRecord),
-  which exposes the phone number associated with a user account.
-- [added] Added the `getPhoneNumber()` method to
-  [`UserInfo`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/UserInfo),
-  which exposes the phone number associated with a user account by a linked
-  identity provider.
-
-### Realtime Database
-- {{changed}} Deprecated the `FirebaseDatabase.setLogLevel()` method. Use SLF4J to configure logging.
-- [changed] Logging a detailed error when the database client fails to authenticate with the backend
-  Firebase servers.
-
-# v5.2.0
-- [added] New factory methods in the
-  [`FirebaseCredentials`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials)
-  class
-  that accept `HttpTransport` and `JsonFactory` arguments. These settings are
-  used when the credentials make HTTP calls to obtain OAuth2 access tokens.
-- [added] New `setHttpTransport()` and `setJsonFactory()` methods in the
-  [`FirebaseOptions`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions)
-  class. These settings are used by all services of the SDK except
-  `FirebaseDatabase`.
-
-# v5.1.0
-
-### Authentication
-
-- [added] A new user management API that allows provisioning and managing
-  Firebase users from Java applications. This API adds `getUser()`,
-  `getUserByEmail()`, `createUser()`, `updateUser()` and `deleteUser()` methods
-  to the [`FirebaseAuth`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth)
-  interface.
-
-# v5.0.1
-
-### Realtime Database
-
-- [changed] Fixed a database API thread leak that made the SDK
-  unstable when running in the Google App Engine environment.
-
-# v5.0.0
-
-### Initialization
-
-- [added] Factory methods in
-  [`FirebaseCredentials`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials)
-  class can now throw `IOExceptions`, providing fail-fast semantics while
-  facilitating simpler error handling.
-- [added] The deprecated `setServiceAccount()` method has been removed
-  from the
-  [`FirebaseOptions.Builder`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder)
-  class in favor of the `setCredential()` method.
-- [added] Trying to initialize the SDK without setting a credential now
-  results in an exception.
-- {{changed}} The
-  [`FirebaseCredential`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredential)
-  interface now returns a new
-  [`GoogleOAuthAccessToken`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/GoogleOAuthAccessToken)
-  type, which encapsulates both token string and its expiry time.
-
-# v4.1.7
-
-- [added] Introducing a new `FirebaseApp.delete()` method, which can
-  be used to gracefully shut down app instances. All app invocations after a
-  call to `delete()` will throw exceptions. Deleted app instances can also be
-  re-initialized with the same name if necessary.
-
-- [changed] Upgraded SDK dependencies. Guava, Google API Client, and JSON
-  libraries that the SDK depends on have been upgraded to more recent versions.
-
-# v4.1.6
-
-### Realtime Database
-
-- [changed] Updated the SDK to select the correct thread pool
-  implementation when running on a regular JVM with App Engine
-  libraries in the `classpath`.
-
-# v4.1.5
-
-### Realtime Database
-
-- [changed] Fixed the invalid SDK version constant in the `FirebaseDatabase`
-  class that was released in v4.1.4.
-
-# v4.1.4
-
-### Authentication
-
-- [changed] Updated the SDK to periodically refresh the OAuth access token
-  internally used by `FirebaseApp`. This reduces the number of authentication
-  failures encountered at runtime by various SDK components (e.g. Realtime Database)
-  to nearly zero. This feature is active by default when running in typical
-  Java environments, or the Google App Engine environment with background
-  threads support.
-
-# v4.1.3
-
-### Realtime Database
-
-- [changed] Updated Realtime Database to properly swap out the ID token used to
-  authenticate the underlying websocket when a new ID token is generated. The
-  websocket connection is still disconnected and reconnected every hour when an
-  ID token expires unless you manually call
-  [`getAccessToken`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredential.html#getAccessToken(boolean))
-  on the `FirebaseCredential` used to authenticate the SDK. In a future
-  release, the SDK will proactively refresh ID tokens automatically before they
-  expire.
-
-
-# v4.1.2
-
-### Initialization
-
-- [changed] Updated `initalizeApp()` to synchronously read from an `InputStream`
-  to avoid issues with closing the stream after initializing the SDK.
-- [changed] Improved confusing error messages when initializing the SDK with
-  a `null` or malformed `InputStream`.
-
-
-# v4.1.1
-### Authentication
-
-- [changed] Fixed a dependency issue which caused the
-  [`verifyIdToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#verifyIdToken(java.lang.String))
-  method to always throw an exception.
-
-
-# v4.1.0
-### Initialization
-
-- {{deprecated}} The
-  [`FirebaseOptions.Builder.setServiceAccount()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder.html#setServiceAccount(java.io.InputStream))
-  method has been deprecated in favor of a new
-  [`FirebaseOptions.Builder.setCredential()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder.html#setCredential(com.google.firebase.auth.FirebaseCredential))
-  method. See [Initialize the SDK](https://firebase.google.com/docs/admin/setup/#initialize_the_sdk) for
-  usage instructions.
-- [added] The new
-  [`FirebaseCredential.fromCertificate()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials.html#fromCertificate(java.io.InputStream))
-  method allows you to authenticate the SDK with a service account certificate
-  file.
-- [added] The new
-  [`FirebaseCredential.fromRefreshToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials.html#fromRefreshToken(java.io.InputStream))
-  method allows you to authenticate the SDK with a Google OAuth2 refresh token.
-- [added] The new
-  [`FirebaseCredential.applicationDefault()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseCredentials.html#applicationDefault())
-  method allows you to authenticate the SDK with Google Application Default
-  Credentials.
-
-### Authentication
-
-- [issue] The
-  [`verifyIdToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#verifyIdToken(java.lang.String))
-  method is broken in this release and throws an exception due to an incorrect
-  dependency. This was fixed in version [`4.1.1`](#4.1.1).
-
-
-# v4.0.4
-
-- [changed] Fixed issue which caused threads to be terminated in Google App
-  Engine after 24 hours, rendering the SDK unresponsive.
-- [changed] Fixed issues which caused asynchronous task execution to fail on
-  automatically-scaled Google App Engine instances.
-
-### Authentication
-
-- [added] Improved error messages and added App Engine support for the
-  [`verifyIdToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#verifyIdToken(java.lang.String))
-  method.
-
-### Realtime Database
-
-- [changed] Fixed a race condition which could occur when new writes are added
-  while the connection is being closed.
-
-
-# v4.0.3
-
-### Initialization
-
-- [changed] Fixed an issue that caused a `null` input to the
-  [`setDatabaseAuthVariableOverride()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder.html#setDatabaseAuthVariableOverride(java.util.Map<java.lang.String, java.lang.Object>))
-  method to be ignored, which caused the app to still have full admin access.
-  Now, passing this value has the expected behavior: the app has unauthenticated
-  access to the Realtime Database, and behaves as if no user is logged into the
-  app.
-
-### Realtime Database
-
-- [changed] Use of the
-  [`updateChildren()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/database/DatabaseReference.html#updateChildren(java.util.Map<java.lang.String, java.lang.Object>))
-  method now only cancels transactions that are directly included in the updated
-  paths (not transactions in adjacent paths). For example, an update at `/move`
-  for a child node `walk` will cancel transactions at `/`, `/move`, and
-  `/move/walk` and in any child nodes under `/move/walk`. But, it will no longer
-  cancel transactions at sibling nodes, such as `/move/run`.
-
-
-# v4.0.2
-
-- [changed] This update restores Java 7 compatibilty for the Admin Java SDK.
-
-
-# v4.0.1
-
-- [changed] Fixed an issue with a missing dependency in the [`4.0.0`](#4.0.0)
-  JAR which caused the Database API to not work.
-- [issue] This version was compiled for Java 8 and does not support Java 7.
-  This was fixed in version [`4.0.2`](#4.0.2).
-
-
-# v4.0.0
-
-- [added] The Admin Java SDK (available on Maven as `firebase-admin`)
-  replaces the pre-existing `firebase-server-sdk` Maven package, which is now
-  deprecated. See
-  [Add the Firebase Admin SDK to your Server](https://firebase.google.com/docs/admin/setup/) to get
-  started.
-- [issue] This version is missing a dependency which causes the Database API
-  to not work. This was fixed in version [`4.0.1`](#4.0.1).
-- [issue] This version was compiled for Java 8 and does not support Java 7.
-  This was fixed in version [`4.0.2`](#4.0.2).
-
-### Authentication
-
-- [changed] The
-  [`createCustomToken()`](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/auth/FirebaseAuth.html#createCustomToken(java.lang.String))
-  method is now asynchronous, returning a `Task<String>` instead of a `String`.
-
-
diff --git a/prepare_release.sh b/prepare_release.sh
index cc5969e..b531557 100755
--- a/prepare_release.sh
+++ b/prepare_release.sh
@@ -75,20 +75,6 @@ RELEASE_BRANCH="release-${TIMESTAMP}"
 echo "[INFO] Creating new release branch: ${RELEASE_BRANCH}"
 git checkout -b ${RELEASE_BRANCH} master
 
-HOST=$(uname)
-echo "[INFO] Updating CHANGELOG.md"
-if [ $HOST == "Darwin" ]; then
-    sed -i "" -e "1 s/# Unreleased//" "CHANGELOG.md"
-else
-    sed -i -e "/# Unreleased/d" "CHANGELOG.md"
-fi
-
-echo -e "# Unreleased\n\n-\n\n# v${VERSION}" | cat - CHANGELOG.md > TEMP_CHANGELOG.md
-mv TEMP_CHANGELOG.md CHANGELOG.md
-git add CHANGELOG.md
-git commit -m "Updating CHANGELOG for ${VERSION} release."
-git push origin ${RELEASE_BRANCH}
-
 
 #################################
 #  RUN MAVEN PREPARATION STEPS  #
diff --git a/src/main/java/com/google/firebase/messaging/AndroidNotification.java b/src/main/java/com/google/firebase/messaging/AndroidNotification.java
index ef286d4..adb84c6 100644
--- a/src/main/java/com/google/firebase/messaging/AndroidNotification.java
+++ b/src/main/java/com/google/firebase/messaging/AndroidNotification.java
@@ -21,9 +21,14 @@ import static com.google.common.base.Preconditions.checkArgument;
 import com.google.api.client.util.Key;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.firebase.internal.NonNull;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Represents the Android-specific notification options that can be included in a {@link Message}.
@@ -69,6 +74,51 @@ public class AndroidNotification {
   
   @Key("image")
   private final String image;
+ 
+  @Key("ticker")
+  private final String ticker;
+
+  @Key("sticky")
+  private final Boolean sticky;
+
+  @Key("event_time")
+  private final String eventTime;
+
+  @Key("local_only")
+  private final Boolean localOnly;
+
+  @Key("notification_priority")
+  private final String priority;
+
+  @Key("vibrate_timings")
+  private final List<String> vibrateTimings;
+
+  @Key("default_vibrate_timings")
+  private final Boolean defaultVibrateTimings;
+
+  @Key("default_sound")
+  private final Boolean defaultSound;
+
+  @Key("light_settings")
+  private final LightSettings lightSettings;
+  
+  @Key("default_light_settings")
+  private final Boolean defaultLightSettings;
+
+  @Key("visibility")
+  private final String visibility;
+
+  @Key("notification_count")
+  private final Integer notificationCount;
+
+  private static final Map<Priority, String> PRIORITY_MAP = 
+      ImmutableMap.<Priority, String>builder()
+          .put(Priority.MIN, "PRIORITY_MIN")
+          .put(Priority.LOW, "PRIORITY_LOW")
+          .put(Priority.DEFAULT, "PRIORITY_DEFAULT")
+          .put(Priority.HIGH, "PRIORITY_HIGH")
+          .put(Priority.MAX, "PRIORITY_MAX")
+          .build();
 
   private AndroidNotification(Builder builder) {
     this.title = builder.title;
@@ -101,6 +151,49 @@ public class AndroidNotification {
     }
     this.channelId = builder.channelId;
     this.image = builder.image;
+    this.ticker = builder.ticker;
+    this.sticky = builder.sticky;
+    this.eventTime = builder.eventTime;
+    this.localOnly = builder.localOnly;
+    if (builder.priority != null) {
+      this.priority = builder.priority.toString();
+    } else {
+      this.priority = null;
+    }
+    if (!builder.vibrateTimings.isEmpty()) {
+      this.vibrateTimings = ImmutableList.copyOf(builder.vibrateTimings);
+    } else {
+      this.vibrateTimings = null;
+    }
+    this.defaultVibrateTimings = builder.defaultVibrateTimings;
+    this.defaultSound = builder.defaultSound;
+    this.lightSettings = builder.lightSettings;
+    this.defaultLightSettings = builder.defaultLightSettings;
+    if (builder.visibility != null) {
+      this.visibility = builder.visibility.name().toLowerCase();
+    } else {
+      this.visibility = null;
+    }
+    this.notificationCount = builder.notificationCount;
+  }
+
+  public enum Priority {
+    MIN,
+    LOW,
+    DEFAULT,
+    HIGH,
+    MAX;
+
+    @Override
+    public String toString() {
+      return PRIORITY_MAP.get(this);
+    }
+  }
+  
+  public enum Visibility {
+    PRIVATE,
+    PUBLIC,
+    SECRET,
   }
 
   /**
@@ -127,6 +220,18 @@ public class AndroidNotification {
     private List<String> titleLocArgs = new ArrayList<>();
     private String channelId;
     private String image;
+    private String ticker;
+    private Boolean sticky;
+    private String eventTime;
+    private Boolean localOnly;
+    private Priority priority;
+    private List<String> vibrateTimings = new ArrayList<>();
+    private Boolean defaultVibrateTimings;
+    private Boolean defaultSound;
+    private LightSettings lightSettings;
+    private Boolean defaultLightSettings;
+    private Visibility visibility;
+    private Integer notificationCount;
 
     private Builder() {}
 
@@ -309,6 +414,186 @@ public class AndroidNotification {
       return this;
     }
 
+    /**
+     * Sets the "ticker" text, which is sent to accessibility services. Prior to API level 21
+     * (Lollipop), sets the text that is displayed in the status bar when the notification 
+     * first arrives.
+     *
+     * @param ticker Ticker name.
+     * @return This builder.
+     */
+    public Builder setTicker(String ticker) {
+      this.ticker = ticker;
+      return this;
+    }
+
+    /**
+     * Sets the sticky flag. When set to false or unset, the notification is automatically 
+     * dismissed when the user clicks it in the panel. When set to true, the notification 
+     * persists even when the user clicks it.
+     *
+     * @param sticky The sticky flag
+     * @return This builder.
+     */
+    public Builder setSticky(boolean sticky) {
+      this.sticky = sticky;
+      return this;
+    }
+
+    /**
+     * For notifications that inform users about events with an absolute time reference, sets
+     * the time that the event in the notification occurred in milliseconds. Notifications
+     * in the panel are sorted by this time. The time is be formated in RFC3339 UTC "Zulu" 
+     * format, accurate to nanoseconds. Example: "2014-10-02T15:01:23.045123456Z". Note that 
+     * since the time is in milliseconds, the last section of the time representation always
+     * has 6 leading zeros.
+     *
+     * @param eventTimeInMillis The event time in milliseconds
+     * @return This builder.
+     */
+    public Builder setEventTimeInMillis(long eventTimeInMillis) {
+      this.eventTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'")
+          .format(new Date(eventTimeInMillis));
+      return this;
+    }
+
+    /**
+     * Sets whether or not this notification is relevant only to the current device. Some 
+     * notifications can be bridged to other devices for remote display, such as a Wear 
+     * OS watch. This hint can be set to recommend this notification not be bridged.
+     *
+     * @param localOnly The "local only" flag
+     * @return This builder.
+     */
+    public Builder setLocalOnly(boolean localOnly) {
+      this.localOnly = localOnly;
+      return this;
+    }
+
+    /**
+     * Sets the relative priority for this notification. Priority is an indication of how much of 
+     * the user's attention should be consumed by this notification. Low-priority notifications 
+     * may be hidden from the user in certain situations, while the user might be interrupted 
+     * for a higher-priority notification.
+     *
+     * @param priority The priority value, one of the values in {MIN, LOW, DEFAULT, HIGH, MAX}
+     * @return This builder.
+     */
+    public Builder setPriority(Priority priority) {
+      this.priority = priority;
+      return this;
+    }
+
+    /**
+     * Sets a list of vibration timings in milliseconds in the array to use. The first value in the 
+     * array indicates the duration to wait before turning the vibrator on. The next value 
+     * indicates the duration to keep the vibrator on. Subsequent values alternate between 
+     * duration to turn the vibrator off and to turn the vibrator on. If {@code vibrate_timings} 
+     * is set and {@code default_vibrate_timings} is set to true, the default value is used instead
+     * of the user-specified {@code vibrate_timings}.
+     * A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".
+     *
+     * @param vibrateTimingsInMillis List of vibration time in milliseconds
+     * @return This builder.
+     */
+    public Builder setVibrateTimingsInMillis(long[] vibrateTimingsInMillis) {
+      List<String> list = new ArrayList<>();
+      for (long value : vibrateTimingsInMillis) {
+        checkArgument(value >= 0, "elements in vibrateTimingsInMillis must not be negative");
+        long seconds = TimeUnit.MILLISECONDS.toSeconds(value);
+        long subsecondNanos = TimeUnit.MILLISECONDS.toNanos(value - seconds * 1000L);
+        if (subsecondNanos > 0) {
+          list.add(String.format("%d.%09ds", seconds, subsecondNanos));
+        } else {
+          list.add(String.format("%ds", seconds));
+        }   
+      }
+      this.vibrateTimings = ImmutableList.copyOf(list);  
+      return this;
+    }
+
+    /**
+     * Sets the whether to use the default vibration timings. If set to true, use the Android 
+     * framework's default vibrate pattern for the notification. Default values are specified 
+     * in {@code config.xml}. If {@code default_vibrate_timings} is set to true and 
+     * {@code vibrate_timings} is also set, the default value is used instead of the 
+     * user-specified {@code vibrate_timings}.
+     *
+     * @param defaultVibrateTimings The flag indicating whether to use the default vibration timings
+     * @return This builder.
+     */
+    public Builder setDefaultVibrateTimings(boolean defaultVibrateTimings) {
+      this.defaultVibrateTimings = defaultVibrateTimings;
+      return this;
+    }
+
+    /**
+     * Sets the whether to use the default sound. If set to true, use the Android framework's 
+     * default sound for the notification. Default values are specified in config.xml.
+     *
+     * @param defaultSound The flag indicating whether to use the default sound
+     * @return This builder.
+     */
+    public Builder setDefaultSound(boolean defaultSound) {
+      this.defaultSound = defaultSound;
+      return this;
+    }
+
+    /**
+     * Sets the settings to control the notification's LED blinking rate and color if LED is 
+     * available on the device. The total blinking time is controlled by the OS.
+     *
+     * @param lightSettings The light settings to use
+     * @return This builder.
+     */
+    public Builder setLightSettings(LightSettings lightSettings) {
+      this.lightSettings = lightSettings;
+      return this;
+    }
+
+    /**
+     * Sets the whether to use the default light settings. If set to true, use the Android
+     * framework's default LED light settings for the notification. Default values are 
+     * specified in config.xml. If {@code default_light_settings} is set to true and 
+     * {@code light_settings} is also set, the user-specified {@code light_settings} is used
+     * instead of the default value.
+     *
+     * @param defaultLightSettings The flag indicating whether to use the default light 
+     *     settings
+     * @return This builder.
+     */
+    public Builder setDefaultLightSettings(boolean defaultLightSettings) {
+      this.defaultLightSettings = defaultLightSettings;
+      return this;
+    }
+
+    /**
+     * Sets the visibility of this notification.
+     *
+     * @param visibility The visibility value. one of the values in {PRIVATE, PUBLIC, SECRET}
+     * @return This builder.
+     */
+    public Builder setVisibility(Visibility visibility) {
+      this.visibility = visibility;
+      return this;
+    }
+
+    /**
+     * Sets the number of items this notification represents. May be displayed as a badge 
+     * count for launchers that support badging. For example, this might be useful if you're 
+     * using just one notification to represent multiple new messages but you want the count 
+     * here to represent the number of total new messages. If zero or unspecified, systems 
+     * that support badging use the default, which is to increment a number displayed on 
+     * the long-press menu each time a new notification arrives.
+     *
+     * @param notificationCount The notification count
+     * @return This builder.
+     */
+    public Builder setNotificationCount(int notificationCount) {
+      this.notificationCount = notificationCount;
+      return this;
+    }
+
     /**
      * Creates a new {@link AndroidNotification} instance from the parameters set on this builder.
      *
diff --git a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
index 7330c27..ee25957 100644
--- a/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
+++ b/src/main/java/com/google/firebase/messaging/FirebaseMessaging.java
@@ -149,7 +149,7 @@ public class FirebaseMessaging {
    * <p>The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
    * value corresponds to the order of input messages.
    *
-   * @param messages A non-null, non-empty list containing up to 100 messages.
+   * @param messages A non-null, non-empty list containing up to 500 messages.
    * @return A {@link BatchResponse} indicating the result of the operation.
    * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
    *     delivery. An exception here indicates a total failure -- i.e. none of the messages in the
@@ -171,7 +171,7 @@ public class FirebaseMessaging {
    * <p>The responses list obtained by calling {@link BatchResponse#getResponses()} on the return
    * value corresponds to the order of input messages.
    *
-   * @param messages A non-null, non-empty list containing up to 100 messages.
+   * @param messages A non-null, non-empty list containing up to 500 messages.
    * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
    * @return A {@link BatchResponse} indicating the result of the operation.
    * @throws FirebaseMessagingException If an error occurs while handing the messages off to FCM for
@@ -186,7 +186,7 @@ public class FirebaseMessaging {
   /**
    * Similar to {@link #sendAll(List)} but performs the operation asynchronously.
    *
-   * @param messages A non-null, non-empty list containing up to 100 messages.
+   * @param messages A non-null, non-empty list containing up to 500 messages.
    * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
    *     the messages have been sent.
    */
@@ -197,7 +197,7 @@ public class FirebaseMessaging {
   /**
    * Similar to {@link #sendAll(List, boolean)} but performs the operation asynchronously.
    *
-   * @param messages A non-null, non-empty list containing up to 100 messages.
+   * @param messages A non-null, non-empty list containing up to 500 messages.
    * @param dryRun A boolean indicating whether to perform a dry run (validation only) of the send.
    * @return @return An {@code ApiFuture} that will complete with a {@link BatchResponse} when
    *     the messages have been sent, or when the emulation has finished.
@@ -284,8 +284,8 @@ public class FirebaseMessaging {
 
     final List<Message> immutableMessages = ImmutableList.copyOf(messages);
     checkArgument(!immutableMessages.isEmpty(), "messages list must not be empty");
-    checkArgument(immutableMessages.size() <= 100,
-        "messages list must not contain more than 100 elements");
+    checkArgument(immutableMessages.size() <= 500,
+        "messages list must not contain more than 500 elements");
     final FirebaseMessagingClient messagingClient = getMessagingClient();
     return new CallableOperation<BatchResponse,FirebaseMessagingException>() {
       @Override
diff --git a/src/main/java/com/google/firebase/messaging/LightSettings.java b/src/main/java/com/google/firebase/messaging/LightSettings.java
new file mode 100644
index 0000000..75a6920
--- /dev/null
+++ b/src/main/java/com/google/firebase/messaging/LightSettings.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * 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 com.google.firebase.messaging;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.api.client.util.Key;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A class representing light settings in an Android Notification.
+ */
+public final class LightSettings {
+  
+  @Key("color")
+  private final LightSettingsColor color;
+  
+  @Key("light_on_duration")
+  private final String lightOnDuration;
+  
+  @Key("light_off_duration")
+  private final String lightOffDuration;
+  
+  private LightSettings(Builder builder) {
+    this.color = builder.color;
+    this.lightOnDuration = builder.lightOnDuration;
+    this.lightOffDuration = builder.lightOffDuration;
+  }
+  
+  /**
+   * Creates a new {@link LightSettings.Builder}.
+   *
+   * @return A {@link LightSettings.Builder} instance.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+  
+  public static class Builder {
+
+    private LightSettingsColor color;
+    private String lightOnDuration;
+    private String lightOffDuration;
+
+    private Builder() {}
+
+    /**
+     * Sets the lightSettingsColor value with a string.   
+     *
+     * @param lightSettingsColor LightSettingsColor specified in the {@code #rrggbb} format.
+     * @return This builder.
+     */
+    public Builder setColorFromString(String color) {
+      this.color = LightSettingsColor.fromString(color);
+      return this;
+    }
+
+    /**
+     * Sets the lightSettingsColor value in the light settings.   
+     *
+     * @param lightSettingsColor Color to be used in the light settings.
+     * @return This builder.
+     */
+    public Builder setColor(LightSettingsColor color) {
+      this.color = color;
+      return this;
+    }
+ 
+    /**
+     * Sets the light on duration in milliseconds.   
+     *
+     * @param lightOnDurationInMillis The time duration in milliseconds for the LED light to be on.
+     * @return This builder.
+     */
+    public Builder setLightOnDurationInMillis(long lightOnDurationInMillis) {
+      this.lightOnDuration = convertToSecondsAndNanosFormat(lightOnDurationInMillis);
+      return this;
+    }
+
+    /**
+     * Sets the light off duration in milliseconds.   
+     *
+     * @param lightOffDurationInMillis The time duration in milliseconds for the LED light to be 
+     *     off.
+     * @return This builder.
+     */
+    public Builder setLightOffDurationInMillis(long lightOffDurationInMillis) {
+      this.lightOffDuration = convertToSecondsAndNanosFormat(lightOffDurationInMillis);
+      return this;
+    }
+
+    private String convertToSecondsAndNanosFormat(long millis) {
+      checkArgument(millis >= 0, "Milliseconds duration must not be negative");
+      long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
+      long subsecondNanos = TimeUnit.MILLISECONDS
+          .toNanos(millis - seconds * 1000L);
+      if (subsecondNanos > 0) {
+        return String.format("%d.%09ds", seconds, subsecondNanos);
+      } else {
+        return String.format("%ds", seconds);
+      }
+    }
+ 
+    /**
+     * Builds a new {@link LightSettings} instance from the fields set on this builder.
+     *
+     * @return A non-null {@link LightSettings}.
+     * @throws IllegalArgumentException If the volume value is out of range.
+     */ 
+    public LightSettings build() {
+      return new LightSettings(this);
+    }
+  }
+}
diff --git a/src/main/java/com/google/firebase/messaging/LightSettingsColor.java b/src/main/java/com/google/firebase/messaging/LightSettingsColor.java
new file mode 100644
index 0000000..cfec649
--- /dev/null
+++ b/src/main/java/com/google/firebase/messaging/LightSettingsColor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * 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 com.google.firebase.messaging;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.api.client.util.Key;
+
+/**
+ * A class representing color in LightSettings.
+ */
+public final class LightSettingsColor {
+  
+  @Key("red")
+  private final Float red;
+  
+  @Key("green")
+  private final Float green;
+  
+  @Key("blue")
+  private final Float blue;
+  
+  @Key("alpha")
+  private final Float alpha;
+  
+  /**
+   * Creates a new {@link LightSettingsColor} using the given red, green, blue, and
+   * alpha values.
+   *
+   * @param red The red component.
+   * @param green The green component.
+   * @param blue The blue component.
+   * @param alpha The alpha component.
+   */
+  public LightSettingsColor(float red, float green, float blue, float alpha) {
+    this.red = red;
+    this.green = green;
+    this.blue = blue;
+    this.alpha = alpha;
+  }
+
+  /**
+   * Creates a new {@link LightSettingsColor} with a string. Alpha of the color will be 
+   * set to 1.
+   *
+   * @param rrggbb LightSettingsColor specified in the {@code #rrggbb} format.
+   * @return A {@link LightSettingsColor} instance.
+   */
+  public static LightSettingsColor fromString(String rrggbb) {
+    checkArgument(rrggbb.matches("^#[0-9a-fA-F]{6}$"), 
+              "LightSettingsColor must be in the form #RRGGBB");
+    float red = (float) Integer.parseInt(rrggbb.substring(1, 3), 16) / 255.0f;
+    float green = (float) Integer.valueOf(rrggbb.substring(3, 5), 16) / 255.0f;
+    float blue = (float) Integer.valueOf(rrggbb.substring(5, 7), 16) / 255.0f;
+    return new LightSettingsColor(red, green, blue, 1.0f);
+  }
+}
diff --git a/src/main/java/com/google/firebase/messaging/MulticastMessage.java b/src/main/java/com/google/firebase/messaging/MulticastMessage.java
index 16434f6..96a6f19 100644
--- a/src/main/java/com/google/firebase/messaging/MulticastMessage.java
+++ b/src/main/java/com/google/firebase/messaging/MulticastMessage.java
@@ -31,7 +31,7 @@ import java.util.Map;
 /**
  * Represents a message that can be sent to multiple devices via Firebase Cloud Messaging (FCM).
  * Contains payload information as well as the list of device registration tokens to which the
- * message should be sent. A single {@code MulticastMessage} may contain up to 100 registration
+ * message should be sent. A single {@code MulticastMessage} may contain up to 500 registration
  * tokens.
  *
  * <p>Instances of this class are thread-safe and immutable. Use {@link MulticastMessage.Builder}
@@ -55,7 +55,7 @@ public class MulticastMessage {
   private MulticastMessage(Builder builder) {
     this.tokens = builder.tokens.build();
     checkArgument(!this.tokens.isEmpty(), "at least one token must be specified");
-    checkArgument(this.tokens.size() <= 100, "no more than 100 tokens can be specified");
+    checkArgument(this.tokens.size() <= 500, "no more than 500 tokens can be specified");
     for (String token : this.tokens) {
       checkArgument(!Strings.isNullOrEmpty(token), "none of the tokens can be null or empty");
     }
@@ -103,7 +103,7 @@ public class MulticastMessage {
     private Builder() {}
 
     /**
-     * Adds a token to which the message should be sent. Up to 100 tokens can be specified on
+     * Adds a token to which the message should be sent. Up to 500 tokens can be specified on
      * a single instance of {@link MulticastMessage}.
      *
      * @param token A non-null, non-empty Firebase device registration token.
@@ -115,7 +115,7 @@ public class MulticastMessage {
     }
 
     /**
-     * Adds a collection of tokens to which the message should be sent. Up to 100 tokens can be
+     * Adds a collection of tokens to which the message should be sent. Up to 500 tokens can be
      * specified on a single instance of {@link MulticastMessage}.
      *
      * @param tokens Collection of Firebase device registration tokens.


Test patch:
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
index 8951d7c..4d9af55 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingIT.java
@@ -114,16 +114,16 @@ public class FirebaseMessagingIT {
   }
 
   @Test
-  public void testSendHundred() throws Exception {
+  public void testSendFiveHundred() throws Exception {
     List<Message> messages = new ArrayList<>();
-    for (int i = 0; i < 100; i++) {
+    for (int i = 0; i < 500; i++) {
       messages.add(Message.builder().setTopic("foo-bar-" + (i % 10)).build());
     }
 
     BatchResponse response = FirebaseMessaging.getInstance().sendAll(messages, true);
 
-    assertEquals(100, response.getResponses().size());
-    assertEquals(100, response.getSuccessCount());
+    assertEquals(500, response.getResponses().size());
+    assertEquals(500, response.getSuccessCount());
     assertEquals(0, response.getFailureCount());
     for (SendResponse sendResponse : response.getResponses()) {
       if (!sendResponse.isSuccessful()) {
diff --git a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
index d5f39ba..bebba08 100644
--- a/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
+++ b/src/test/java/com/google/firebase/messaging/FirebaseMessagingTest.java
@@ -296,7 +296,7 @@ public class FirebaseMessagingTest {
     MockFirebaseMessagingClient client = MockFirebaseMessagingClient.fromMessageId(null);
     FirebaseMessaging messaging = getMessagingForSend(Suppliers.ofInstance(client));
     ImmutableList.Builder<Message> listBuilder = ImmutableList.builder();
-    for (int i = 0; i < 101; i++) {
+    for (int i = 0; i < 501; i++) {
       listBuilder.add(Message.builder().setTopic("topic").build());
     }
 
diff --git a/src/test/java/com/google/firebase/messaging/MessageTest.java b/src/test/java/com/google/firebase/messaging/MessageTest.java
index 556f2d9..7c8f528 100644
--- a/src/test/java/com/google/firebase/messaging/MessageTest.java
+++ b/src/test/java/com/google/firebase/messaging/MessageTest.java
@@ -28,6 +28,9 @@ import com.google.common.collect.ImmutableMap;
 import com.google.firebase.messaging.AndroidConfig.Priority;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -796,6 +799,87 @@ public class MessageTest {
             .build();
     assertJsonEquals(expected, message);
   }
+  
+  @Test
+  public void testInvalidColorInAndroidNotificationLightSettings() throws IOException {
+    try {
+      LightSettings.Builder lightSettingsBuilder = LightSettings.builder()
+                      .setColorFromString("#01020K")
+                      .setLightOnDurationInMillis(1002L)
+                      .setLightOffDurationInMillis(1003L);
+
+      lightSettingsBuilder.build();
+      fail("No error thrown for invalid notification");
+    } catch (IllegalArgumentException expected) {
+      // expected
+    }
+  }
+  
+  @Test
+  public void testExtendedAndroidNotificationParameters() throws IOException {
+    long[] vibrateTimings = {1000L, 1001L};
+    Message message = Message.builder()
+        .setNotification(new Notification("title", "body"))
+        .setAndroidConfig(AndroidConfig.builder()
+            .setNotification(AndroidNotification.builder()
+                .setTitle("android-title")
+                .setBody("android-body")
+                .setTicker("ticker")
+                .setSticky(true)
+                .setEventTimeInMillis(1546304523123L)
+                .setLocalOnly(true)
+                .setPriority(AndroidNotification.Priority.HIGH)
+                .setVibrateTimingsInMillis(vibrateTimings)
+                .setDefaultVibrateTimings(false)
+                .setDefaultSound(false)
+                .setLightSettings(LightSettings.builder()
+                    .setColorFromString("#336699")
+                    .setLightOnDurationInMillis(1002L)
+                    .setLightOffDurationInMillis(1003L)
+                    .build())
+                .setDefaultLightSettings(false)
+                .setVisibility(AndroidNotification.Visibility.PUBLIC)
+                .setNotificationCount(10)
+                .build())
+            .build())
+        .setTopic("test-topic")
+        .build();
+    Map<String, Object> notification = ImmutableMap.<String, Object>builder()
+        .put("title", "title")
+        .put("body", "body")
+        .build();
+    String eventTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS'Z'")
+        .format(new Date(1546304523123L));
+    Map<String, Object> androidConfig = ImmutableMap.<String, Object>builder()
+        .put("notification", ImmutableMap.<String, Object>builder()
+            .put("title", "android-title")
+            .put("body", "android-body")
+            .put("ticker", "ticker")
+            .put("sticky", true)
+            .put("event_time", eventTime)
+            .put("local_only", true)
+            .put("notification_priority", "PRIORITY_HIGH")
+            .put("vibrate_timings", ImmutableList.of("1s", "1.001000000s"))
+            .put("default_vibrate_timings", false)
+            .put("default_sound", false)
+            .put("light_settings", ImmutableMap.<String, Object>builder()
+                .put("color", ImmutableMap.<String, Object>builder()
+                    .put("red", new BigDecimal(new BigInteger("2"), 1))
+                    .put("green", new BigDecimal(new BigInteger("4"), 1))
+                    .put("blue", new BigDecimal(new BigInteger("6"), 1))
+                    .put("alpha", new BigDecimal(new BigInteger("10"), 1))
+                    .build())
+                .put("light_on_duration", "1.002000000s")
+                .put("light_off_duration", "1.003000000s")
+                .build())
+            .put("default_light_settings", false)
+            .put("visibility", "public")
+            .put("notification_count", new BigDecimal(10))
+            .build())
+        .build();
+    assertJsonEquals(ImmutableMap.of(
+        "topic", "test-topic", "notification", notification, "android", androidConfig), message);
+  }
 
   private static void assertJsonEquals(
       Map expected, Object actual) throws IOException {
diff --git a/src/test/java/com/google/firebase/messaging/MulticastMessageTest.java b/src/test/java/com/google/firebase/messaging/MulticastMessageTest.java
index 5cd0531..25018ed 100644
--- a/src/test/java/com/google/firebase/messaging/MulticastMessageTest.java
+++ b/src/test/java/com/google/firebase/messaging/MulticastMessageTest.java
@@ -70,12 +70,12 @@ public class MulticastMessageTest {
   @Test
   public void testTooManyTokens() {
     MulticastMessage.Builder builder = MulticastMessage.builder();
-    for (int i = 0; i < 101; i++) {
+    for (int i = 0; i < 501; i++) {
       builder.addToken("token" + i);
     }
     try {
       builder.build();
-      fail("No error thrown for more than 100 tokens");
+      fail("No error thrown for more than 500 tokens");
     } catch (IllegalArgumentException expected) {
       // expected
     }
diff --git a/src/test/java/com/google/firebase/snippets/FirebaseMessagingSnippets.java b/src/test/java/com/google/firebase/snippets/FirebaseMessagingSnippets.java
index eb94021..d6e4417 100644
--- a/src/test/java/com/google/firebase/snippets/FirebaseMessagingSnippets.java
+++ b/src/test/java/com/google/firebase/snippets/FirebaseMessagingSnippets.java
@@ -122,7 +122,7 @@ public class FirebaseMessagingSnippets {
     String registrationToken = "YOUR_REGISTRATION_TOKEN";
 
     // [START send_all]
-    // Create a list containing up to 100 messages.
+    // Create a list containing up to 500 messages.
     List<Message> messages = Arrays.asList(
         Message.builder()
             .setNotification(new Notification("Price drop", "5% off all electronics"))