Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3389376
D7874.id27188.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D7874.id27188.diff
View Options
diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -73,8 +73,12 @@
threadID,
);
deliveryPromises[id] = fcmPush({
- notification,
- deviceTokens: delivery.androidDeviceTokens,
+ targetedNotifications: delivery.androidDeviceTokens.map(
+ deviceToken => ({
+ deviceToken,
+ notification,
+ }),
+ ),
codeVersion: null,
});
} else if (delivery.deviceType === 'ios') {
@@ -103,8 +107,10 @@
threadID,
);
deliveryPromises[id] = fcmPush({
- notification,
- deviceTokens,
+ targetedNotifications: deviceTokens.map(deviceToken => ({
+ deviceToken,
+ notification,
+ })),
codeVersion,
});
}
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -41,10 +41,16 @@
import { promiseAll } from 'lib/utils/promises.js';
import { tID, tPlatformDetails, tShape } from 'lib/utils/validation-utils.js';
-import { prepareEncryptedIOSNotifications } from './crypto.js';
+import {
+ prepareEncryptedIOSNotifications,
+ prepareEncryptedAndroidNotifications,
+} from './crypto.js';
import { getAPNsNotificationTopic } from './providers.js';
import { rescindPushNotifs } from './rescind.js';
-import type { TargetedAPNsNotification } from './types.js';
+import type {
+ TargetedAPNsNotification,
+ TargetedAndroidNotification,
+} from './types.js';
import {
apnPush,
fcmPush,
@@ -238,18 +244,20 @@
platformDetails,
);
const deliveryPromise = (async () => {
- const notification = await prepareAndroidNotification({
- notifTexts,
- newRawMessageInfos: shimmedNewRawMessageInfos,
- threadID: threadInfo.id,
- collapseKey: notifInfo.collapseKey,
- badgeOnly,
- unreadCount: unreadCounts[userID],
- platformDetails,
- dbID,
- });
- const deviceTokens = devices.map(({ deviceToken }) => deviceToken);
- return await sendAndroidNotification(notification, deviceTokens, {
+ const targetedNotifications = await prepareAndroidNotification(
+ {
+ notifTexts,
+ newRawMessageInfos: shimmedNewRawMessageInfos,
+ threadID: threadInfo.id,
+ collapseKey: notifInfo.collapseKey,
+ badgeOnly,
+ unreadCount: unreadCounts[userID],
+ platformDetails,
+ dbID,
+ },
+ devices,
+ );
+ return await sendAndroidNotification(targetedNotifications, {
...notificationInfo,
codeVersion,
});
@@ -755,7 +763,8 @@
});
async function prepareAndroidNotification(
inputData: AndroidNotifInputData,
-): Promise<Object> {
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
const convertedData = validateOutput(
inputData.platformDetails,
androidNotifInputDataValidator,
@@ -772,6 +781,13 @@
dbID,
} = convertedData;
+ const isTextNotification = newRawMessageInfos.every(
+ newRawMessageInfo => newRawMessageInfo.type === messageTypes.TEXT,
+ );
+
+ const shouldBeEncrypted =
+ isTextNotification && !collapseKey && codeVersion && codeVersion > 222;
+
const notifID = collapseKey ? collapseKey : dbID;
const { merged, ...rest } = notifTexts;
const notification = {
@@ -802,22 +818,44 @@
data: { ...notification.data, messageInfos },
};
- if (
- Buffer.byteLength(JSON.stringify(copyWithMessageInfos)) <=
- fcmMaxNotificationPayloadByteSize
- ) {
- return copyWithMessageInfos;
- }
+ const evaluateAndSelectNotification = (notif, notifWithMessageInfos) => {
+ if (
+ Buffer.byteLength(JSON.stringify(notifWithMessageInfos)) <=
+ fcmMaxNotificationPayloadByteSize
+ ) {
+ return notifWithMessageInfos;
+ }
+ if (
+ Buffer.byteLength(JSON.stringify(notif)) >
+ fcmMaxNotificationPayloadByteSize
+ ) {
+ console.warn(
+ `Android notification ${notifID} exceeds size limit, even with messageInfos omitted`,
+ );
+ }
+ return notif;
+ };
- if (
- Buffer.byteLength(JSON.stringify(notification)) >
- fcmMaxNotificationPayloadByteSize
- ) {
- console.warn(
- `Android notification ${notifID} exceeds size limit, even with messageInfos omitted`,
- );
+ const deviceTokens = devices.map(({ deviceToken }) => deviceToken);
+ if (shouldBeEncrypted) {
+ const cookieIDs = devices.map(({ cookieID }) => cookieID);
+ const [notifications, notificationsWithMessageInfos] = await Promise.all([
+ prepareEncryptedAndroidNotifications(cookieIDs, notification),
+ prepareEncryptedAndroidNotifications(cookieIDs, copyWithMessageInfos),
+ ]);
+ return notificationsWithMessageInfos.map((notif, idx) => ({
+ notification: evaluateAndSelectNotification(notifications[idx], notif),
+ deviceToken: deviceTokens[idx],
+ }));
}
- return notification;
+ const notificationToSend = evaluateAndSelectNotification(
+ notification,
+ copyWithMessageInfos,
+ );
+ return deviceTokens.map(deviceToken => ({
+ notification: notificationToSend,
+ deviceToken,
+ }));
}
type WebNotifInputData = {
@@ -975,8 +1013,7 @@
invalidTokens?: $ReadOnlyArray<string>,
};
async function sendAndroidNotification(
- notification: Object,
- deviceTokens: $ReadOnlyArray<string>,
+ targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>,
notificationInfo: NotificationInfo,
): Promise<AndroidResult> {
const collapseKey = notificationInfo.collapseKey
@@ -984,11 +1021,13 @@
: null; // for Flow...
const { source, codeVersion } = notificationInfo;
const response = await fcmPush({
- notification,
- deviceTokens,
+ targetedNotifications,
collapseKey,
codeVersion,
});
+ const deviceTokens = targetedNotifications.map(
+ ({ deviceToken }) => deviceToken,
+ );
const androidIDs = response.fcmIDs ? response.fcmIDs : [];
const delivery: AndroidDelivery = {
source,
@@ -1234,15 +1273,31 @@
? { badge: unreadCount.toString() }
: { badge: unreadCount.toString(), badgeOnly: '1' };
const notification = { data: notificationData };
- const deviceTokens = deviceInfos.map(({ deviceToken }) => deviceToken);
- deliveryPromises.push(
- sendAndroidNotification(notification, deviceTokens, {
+ const deliveryPromise = (async () => {
+ const cookieIDs = deviceInfos.map(({ cookieID }) => cookieID);
+ let notificationsArray;
+ if (codeVersion > 222) {
+ notificationsArray = await prepareEncryptedAndroidNotifications(
+ cookieIDs,
+ notification,
+ );
+ } else {
+ notificationsArray = cookieIDs.map(() => notification);
+ }
+ const targetedNotifications = deviceInfos.map(
+ ({ deviceToken }, idx) => ({
+ deviceToken,
+ notification: notificationsArray[idx],
+ }),
+ );
+ return await sendAndroidNotification(targetedNotifications, {
source,
dbID,
userID,
codeVersion,
- }),
- );
+ });
+ })();
+ deliveryPromises.push(deliveryPromise);
}
}
diff --git a/keyserver/src/push/types.js b/keyserver/src/push/types.js
--- a/keyserver/src/push/types.js
+++ b/keyserver/src/push/types.js
@@ -14,3 +14,8 @@
+[string]: string,
},
};
+
+export type TargetedAndroidNotification = {
+ +notification: AndroidNotification,
+ +deviceToken: string,
+};
diff --git a/keyserver/src/push/utils.js b/keyserver/src/push/utils.js
--- a/keyserver/src/push/utils.js
+++ b/keyserver/src/push/utils.js
@@ -23,7 +23,7 @@
ensureWebPushInitialized,
getWNSToken,
} from './providers.js';
-import type { TargetedAPNsNotification } from './types.js';
+import type { TargetedAPNsNotification, AndroidNotification } from './types.js';
import { dbQuery, SQL } from '../database/database.js';
const fcmTokenInvalidationErrors = new Set([
@@ -102,13 +102,14 @@
+invalidTokens?: $ReadOnlyArray<string>,
};
async function fcmPush({
- notification,
- deviceTokens,
+ targetedNotifications,
collapseKey,
codeVersion,
}: {
- +notification: Object,
- +deviceTokens: $ReadOnlyArray<string>,
+ +targetedNotifications: $ReadOnlyArray<{
+ +notification: AndroidNotification,
+ +deviceToken: string,
+ }>,
+codeVersion: ?number,
+collapseKey?: ?string,
}): Promise<FCMPushResult> {
@@ -131,7 +132,7 @@
// won't explain which of the device tokens is invalid. So we're forced to
// avoid the multicast functionality and call it once per deviceToken.
const promises = [];
- for (const deviceToken of deviceTokens) {
+ for (const { notification, deviceToken } of targetedNotifications) {
promises.push(
fcmSinglePush(fcmProvider, notification, deviceToken, options),
);
@@ -146,7 +147,7 @@
for (const error of pushResult.errors) {
errors.push(error);
if (fcmTokenInvalidationErrors.has(error.errorInfo.code)) {
- invalidTokens.push(deviceTokens[i]);
+ invalidTokens.push(targetedNotifications[i].deviceToken);
}
}
for (const id of pushResult.fcmIDs) {
diff --git a/keyserver/src/session/cookies.js b/keyserver/src/session/cookies.js
--- a/keyserver/src/session/cookies.js
+++ b/keyserver/src/session/cookies.js
@@ -828,7 +828,8 @@
): Promise<boolean> {
if (
!viewer.platformDetails ||
- viewer.platformDetails.platform !== 'ios' ||
+ (viewer.platformDetails.platform !== 'ios' &&
+ viewer.platformDetails.platform !== 'android') ||
!viewer.platformDetails.codeVersion ||
viewer.platformDetails.codeVersion < 222
) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 6:50 PM (21 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2601999
Default Alt Text
D7874.id27188.diff (9 KB)
Attached To
Mode
D7874: Encrypt Android notifications on the keyserver before sending them
Attached
Detach File
Event Timeline
Log In to Comment