Chromium Code Reviews| Index: components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java |
| diff --git a/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..eb544c2f6661d41048ea82c2ef57ba5ff506855e |
| --- /dev/null |
| +++ b/components/gcm_driver/android/java/src/org/chromium/components/gcm_driver/GCMMessage.java |
| @@ -0,0 +1,171 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package org.chromium.components.gcm_driver; |
| + |
| +import android.annotation.TargetApi; |
| +import android.os.Build; |
| +import android.os.Bundle; |
| +import android.os.PersistableBundle; |
| + |
| +import org.chromium.base.Log; |
| + |
| +import java.util.ArrayList; |
| +import java.util.List; |
| + |
| +import javax.annotation.Nullable; |
| + |
| +/** |
| + * Represents the contents of a GCM Message that is to be handled by the GCM Driver. Can be created |
| + * based on data received from GCM, or serialized and deserialized to and from a PersistableBundle. |
| + */ |
| +public class GCMMessage { |
|
awdf
2017/02/16 21:26:48
nit: worth implementing hashcode and equals()? mig
Peter Beverloo
2017/02/17 15:43:59
I really don't feel strongly, but mostly due to my
|
| + private static final String TAG = "GCMMessage"; |
| + |
| + /** |
| + * Keys used to store information in the persistable bundle for serialization purposes. |
| + */ |
| + private static final String BUNDLE_KEY_APP_ID = "appId"; |
| + private static final String BUNDLE_KEY_COLLAPSE_KEY = "collapseKey"; |
| + private static final String BUNDLE_KEY_DATA = "data"; |
| + private static final String BUNDLE_KEY_RAW_DATA = "rawData"; |
| + private static final String BUNDLE_KEY_SENDER_ID = "senderId"; |
| + |
| + private String mSenderId; |
| + private String mAppId; |
| + |
| + @Nullable |
| + private String mCollapseKey; |
| + @Nullable |
| + private byte[] mRawData; |
| + |
| + /** |
| + * Array that contains pairs of entries in the format of {key, value}. |
| + */ |
| + private String[] mDataKeysAndValuesArray; |
| + |
| + /** |
| + * Validates that all required fields have been set in the given bundle. |
| + */ |
| + @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| + public static boolean validatePersistableBundle(PersistableBundle persistableBundle) { |
| + return persistableBundle.containsKey(BUNDLE_KEY_APP_ID) |
| + && persistableBundle.containsKey(BUNDLE_KEY_COLLAPSE_KEY) |
| + && persistableBundle.containsKey(BUNDLE_KEY_DATA) |
| + && persistableBundle.containsKey(BUNDLE_KEY_RAW_DATA) |
| + && persistableBundle.containsKey(BUNDLE_KEY_SENDER_ID); |
| + } |
| + |
| + /** |
| + * Creates a GCMMessage object based on data received from GCM. The extras will be filtered. |
| + */ |
| + public GCMMessage(String senderId, Bundle extras) { |
| + final String bundleCollapseKey = "collapse_key"; |
| + final String bundleGcmplex = "com.google.ipc.invalidation.gcmmplex."; |
| + final String bundleRawData = "rawData"; |
| + final String bundleSenderId = "from"; |
| + final String bundleSubtype = "subtype"; |
| + |
| + if (!extras.containsKey(bundleSubtype)) { |
| + Log.w(TAG, "Received push message with no subtype"); |
| + return; |
|
awdf
2017/02/16 21:26:48
shouldn't we throw an IllegalArgumentException or
Peter Beverloo
2017/02/17 15:43:59
Done. I've added a try/catch in ChromeGcmListenerS
|
| + } |
| + |
| + mSenderId = senderId; |
| + mAppId = extras.getString(bundleSubtype); |
| + |
| + mCollapseKey = extras.getString(bundleCollapseKey); // May be null. |
| + mRawData = extras.getByteArray(bundleRawData); // May be null. |
| + |
| + List<String> dataKeysAndValues = new ArrayList<String>(); |
| + for (String key : extras.keySet()) { |
| + if (key.equals(bundleSubtype) || key.equals(bundleSenderId) |
| + || key.equals(bundleCollapseKey) || key.equals(bundleRawData) |
| + || key.startsWith(bundleGcmplex)) { |
| + continue; |
| + } |
| + |
| + Object value = extras.get(key); |
| + if (!(value instanceof String)) { |
| + continue; |
| + } |
| + |
| + dataKeysAndValues.add(key); |
| + dataKeysAndValues.add((String) value); |
| + } |
| + |
| + mDataKeysAndValuesArray = dataKeysAndValues.toArray(new String[dataKeysAndValues.size()]); |
| + } |
| + |
| + /** |
| + * Creates a GCMMessage object based on the given bundle. Assumes that the bundle has previously |
| + * been created through {@link #toPersistableBundle}. |
| + */ |
| + @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| + public GCMMessage(PersistableBundle persistableBundle) { |
| + mSenderId = persistableBundle.getString(BUNDLE_KEY_SENDER_ID); |
| + mAppId = persistableBundle.getString(BUNDLE_KEY_APP_ID); |
| + mCollapseKey = persistableBundle.getString(BUNDLE_KEY_COLLAPSE_KEY); |
| + |
| + // The rawData field needs to distinguish between {not set, set but empty, set with data}. |
| + String rawDataString = persistableBundle.getString(BUNDLE_KEY_RAW_DATA); |
| + if (rawDataString != null) { |
| + if (rawDataString.length() > 0) { |
| + mRawData = rawDataString.getBytes(); |
| + } else { |
| + mRawData = new byte[0]; |
| + } |
| + } |
| + |
| + mDataKeysAndValuesArray = persistableBundle.getStringArray(BUNDLE_KEY_DATA); |
| + } |
| + |
| + public String getSenderId() { |
| + return mSenderId; |
| + } |
| + |
| + public String getAppId() { |
| + return mAppId; |
| + } |
| + |
| + @Nullable |
| + public String getCollapseKey() { |
| + return mCollapseKey; |
| + } |
| + |
| + @Nullable |
| + public byte[] getRawData() { |
| + return mRawData; |
| + } |
| + |
| + public String[] getDataKeysAndValuesArray() { |
| + return mDataKeysAndValuesArray; |
| + } |
| + |
| + /** |
| + * Serializes the contents of this GCM Message to a new persistable bundle that can be stored, |
| + * for example for purposes of scheduling a job. |
| + */ |
| + @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| + public PersistableBundle toPersistableBundle() { |
| + PersistableBundle persistableBundle = new PersistableBundle(); |
| + persistableBundle.putString(BUNDLE_KEY_SENDER_ID, mSenderId); |
| + persistableBundle.putString(BUNDLE_KEY_APP_ID, mAppId); |
| + persistableBundle.putString(BUNDLE_KEY_COLLAPSE_KEY, mCollapseKey); |
| + |
| + // The rawData field needs to distinguish between {not set, set but empty, set with data}. |
| + if (mRawData != null) { |
| + if (mRawData.length > 0) { |
| + persistableBundle.putString(BUNDLE_KEY_RAW_DATA, new String(mRawData)); |
| + } else { |
| + persistableBundle.putString(BUNDLE_KEY_RAW_DATA, new String()); |
| + } |
| + } else { |
| + persistableBundle.putString(BUNDLE_KEY_RAW_DATA, null); |
| + } |
| + |
| + persistableBundle.putStringArray(BUNDLE_KEY_DATA, mDataKeysAndValuesArray); |
| + return persistableBundle; |
| + } |
| +} |