Page MenuHomePhabricator

D11858.diff
No OneTemporary

D11858.diff

diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/keyserver/src/push/crypto.js
@@ -17,8 +17,9 @@
import { toBase64URL } from 'lib/utils/base64.js';
import type {
- AndroidNotification,
- AndroidNotificationPayload,
+ AndroidVisualNotification,
+ AndroidVisualNotificationPayload,
+ AndroidBadgeOnlyNotification,
AndroidNotificationRescind,
NotificationTargetDevice,
} from './types.js';
@@ -197,18 +198,19 @@
}
}
-async function encryptAndroidNotification(
+async function encryptAndroidVisualNotification(
cookieID: string,
- notification: AndroidNotification,
- notificationSizeValidator?: AndroidNotification => boolean,
+ notification: AndroidVisualNotification,
+ notificationSizeValidator?: AndroidVisualNotification => boolean,
blobHolder?: ?string,
): Promise<{
- +notification: AndroidNotification,
+ +notification: AndroidVisualNotification,
+payloadSizeExceeded: boolean,
+encryptionOrder?: number,
}> {
- const { id, keyserverID, badgeOnly, ...rest } = notification.data;
- let unencryptedData = { badgeOnly, keyserverID };
+ const { id, keyserverID, ...rest } = notification.data;
+
+ let unencryptedData = { keyserverID };
if (id) {
unencryptedData = { ...unencryptedData, id };
}
@@ -221,7 +223,7 @@
let payloadSizeValidator;
if (notificationSizeValidator) {
payloadSizeValidator = (
- payload: AndroidNotificationPayload | { +encryptedPayload: string },
+ payload: AndroidVisualNotificationPayload | { +encryptedPayload: string },
) => {
return notificationSizeValidator({
data: { ...unencryptedData, ...payload },
@@ -246,10 +248,10 @@
};
}
-async function encryptAndroidNotificationRescind(
+async function encryptAndroidSilentNotification(
cookieID: string,
- notification: AndroidNotificationRescind,
-): Promise<AndroidNotificationRescind> {
+ notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
+): Promise<AndroidNotificationRescind | AndroidBadgeOnlyNotification> {
// We don't validate payload size for rescind
// since they are expected to be small and
// never exceed any FCM limit
@@ -258,8 +260,23 @@
cookieID,
unencryptedPayload,
);
+ if (resultPayload.encryptedPayload) {
+ return {
+ data: { keyserverID, ...resultPayload },
+ };
+ }
+
+ if (resultPayload.rescind) {
+ return {
+ data: { keyserverID, ...resultPayload },
+ };
+ }
+
return {
- data: { keyserverID, ...resultPayload },
+ data: {
+ keyserverID,
+ ...resultPayload,
+ },
};
}
@@ -383,22 +400,24 @@
return Promise.all(notificationPromises);
}
-function prepareEncryptedAndroidNotifications(
+function prepareEncryptedAndroidVisualNotifications(
devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: AndroidNotification,
- notificationSizeValidator?: (notification: AndroidNotification) => boolean,
+ notification: AndroidVisualNotification,
+ notificationSizeValidator?: (
+ notification: AndroidVisualNotification,
+ ) => boolean,
): Promise<
$ReadOnlyArray<{
+cookieID: string,
+deviceToken: string,
- +notification: AndroidNotification,
+ +notification: AndroidVisualNotification,
+payloadSizeExceeded: boolean,
+encryptionOrder?: number,
}>,
> {
const notificationPromises = devices.map(
async ({ deviceToken, cookieID, blobHolder }) => {
- const notif = await encryptAndroidNotification(
+ const notif = await encryptAndroidVisualNotification(
cookieID,
notification,
notificationSizeValidator,
@@ -410,20 +429,20 @@
return Promise.all(notificationPromises);
}
-function prepareEncryptedAndroidNotificationRescinds(
+function prepareEncryptedAndroidSilentNotifications(
devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: AndroidNotificationRescind,
+ notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
): Promise<
$ReadOnlyArray<{
+cookieID: string,
+deviceToken: string,
- +notification: AndroidNotificationRescind,
+ +notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
+encryptionOrder?: number,
}>,
> {
const notificationPromises = devices.map(
async ({ deviceToken, cookieID }) => {
- const notif = await encryptAndroidNotificationRescind(
+ const notif = await encryptAndroidSilentNotification(
cookieID,
notification,
);
@@ -500,8 +519,8 @@
export {
prepareEncryptedAPNsNotifications,
prepareEncryptedIOSNotificationRescind,
- prepareEncryptedAndroidNotifications,
- prepareEncryptedAndroidNotificationRescinds,
+ prepareEncryptedAndroidVisualNotifications,
+ prepareEncryptedAndroidSilentNotifications,
prepareEncryptedWebNotifications,
prepareEncryptedWNSNotifications,
encryptBlobPayload,
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
@@ -12,7 +12,7 @@
import { tID } from 'lib/utils/validation-utils.js';
import {
- prepareEncryptedAndroidNotificationRescinds,
+ prepareEncryptedAndroidSilentNotifications,
prepareEncryptedIOSNotificationRescind,
} from './crypto.js';
import { getAPNsNotificationTopic } from './providers.js';
@@ -378,12 +378,16 @@
keyserverID,
},
};
- return await conditionallyEncryptNotification(
+ const targetedRescinds = await conditionallyEncryptNotification(
notification,
codeVersion,
devices,
- prepareEncryptedAndroidNotificationRescinds,
+ prepareEncryptedAndroidSilentNotifications,
);
+ return targetedRescinds.map(targetedRescind => ({
+ ...targetedRescind,
+ priority: 'normal',
+ }));
}
export { rescindPushNotifs };
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
@@ -43,7 +43,8 @@
import { tID, tPlatformDetails, tShape } from 'lib/utils/validation-utils.js';
import {
- prepareEncryptedAndroidNotifications,
+ prepareEncryptedAndroidVisualNotifications,
+ prepareEncryptedAndroidSilentNotifications,
prepareEncryptedAPNsNotifications,
prepareEncryptedWebNotifications,
prepareEncryptedWNSNotifications,
@@ -51,7 +52,7 @@
import { getAPNsNotificationTopic } from './providers.js';
import { rescindPushNotifs } from './rescind.js';
import type {
- AndroidNotification,
+ AndroidVisualNotification,
NotificationTargetDevice,
TargetedAndroidNotification,
TargetedAPNsNotification,
@@ -367,7 +368,7 @@
);
const preparePromise: Promise<$ReadOnlyArray<PreparePushResult>> =
(async () => {
- const targetedNotifications = await prepareAndroidNotification(
+ const targetedNotifications = await prepareAndroidVisualNotification(
{
keyserverID,
notifTexts,
@@ -1167,7 +1168,7 @@
...commonNativeNotifInputDataValidator.meta.props,
dbID: t.String,
});
-async function prepareAndroidNotification(
+async function prepareAndroidVisualNotification(
inputData: AndroidNotifInputData,
devices: $ReadOnlyArray<NotificationTargetDevice>,
): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
@@ -1239,6 +1240,7 @@
data: { ...notification.data, messageInfos },
};
+ const priority = 'high';
if (!shouldBeEncrypted) {
const notificationToSend =
Buffer.byteLength(JSON.stringify(copyWithMessageInfos)) <=
@@ -1247,12 +1249,13 @@
: notification;
return devices.map(({ deviceToken }) => ({
+ priority,
notification: notificationToSend,
deviceToken,
}));
}
- const notificationsSizeValidator = (notif: AndroidNotification) => {
+ const notificationsSizeValidator = (notif: AndroidVisualNotification) => {
const serializedNotif = JSON.stringify(notif);
return (
!serializedNotif ||
@@ -1260,11 +1263,12 @@
);
};
- const notifsWithMessageInfos = await prepareEncryptedAndroidNotifications(
- devices,
- copyWithMessageInfos,
- notificationsSizeValidator,
- );
+ const notifsWithMessageInfos =
+ await prepareEncryptedAndroidVisualNotifications(
+ devices,
+ copyWithMessageInfos,
+ notificationsSizeValidator,
+ );
const devicesWithExcessiveSizeNoHolders = notifsWithMessageInfos
.filter(({ payloadSizeExceeded }) => payloadSizeExceeded)
@@ -1273,6 +1277,7 @@
if (devicesWithExcessiveSizeNoHolders.length === 0) {
return notifsWithMessageInfos.map(
({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
notification: notif,
deviceToken,
encryptionOrder,
@@ -1319,14 +1324,16 @@
}));
}
- const notifsWithoutMessageInfos = await prepareEncryptedAndroidNotifications(
- devicesWithExcessiveSize,
- notification,
- );
+ const notifsWithoutMessageInfos =
+ await prepareEncryptedAndroidVisualNotifications(
+ devicesWithExcessiveSize,
+ notification,
+ );
const targetedNotifsWithMessageInfos = notifsWithMessageInfos
.filter(({ payloadSizeExceeded }) => !payloadSizeExceeded)
.map(({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
notification: notif,
deviceToken,
encryptionOrder,
@@ -1334,6 +1341,7 @@
const targetedNotifsWithoutMessageInfos = notifsWithoutMessageInfos.map(
({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
notification: notif,
deviceToken,
encryptionOrder,
@@ -1832,22 +1840,25 @@
if (androidVersionsToTokens) {
for (const [versionKey, deviceInfos] of androidVersionsToTokens) {
const { codeVersion, stateVersion } = stringToVersionKey(versionKey);
- const notificationData =
- codeVersion < 69
- ? { badge: unreadCount.toString() }
- : { badge: unreadCount.toString(), badgeOnly: '1' };
+ const notificationData = {
+ badge: unreadCount.toString(),
+ badgeOnly: '1',
+ };
const notification = {
data: { ...notificationData, keyserverID },
};
const preparePromise: Promise<PreparePushResult[]> = (async () => {
let targetedNotifications: $ReadOnlyArray<TargetedAndroidNotification>;
+ const priority = 'normal';
if (codeVersion > 222) {
- const notificationsArray = await prepareEncryptedAndroidNotifications(
- deviceInfos,
- notification,
- );
+ const notificationsArray =
+ await prepareEncryptedAndroidSilentNotifications(
+ deviceInfos,
+ notification,
+ );
targetedNotifications = notificationsArray.map(
({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
notification: notif,
deviceToken,
encryptionOrder,
@@ -1855,6 +1866,7 @@
);
} else {
targetedNotifications = deviceInfos.map(({ deviceToken }) => ({
+ priority,
deviceToken,
notification,
}));
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,38 +14,49 @@
+encryptionOrder?: number,
};
-type AndroidNotificationPayloadBase = {
+export type AndroidVisualNotificationPayloadBase = $ReadOnly<{
+badge: string,
- +body?: string,
- +title?: string,
+ +body: string,
+ +title: string,
+prefix?: string,
- +threadID?: string,
+ +threadID: string,
+collapseKey?: string,
+ +badgeOnly?: '0',
+encryptionFailed?: '1',
-};
+}>;
-export type AndroidNotificationPayload =
+export type AndroidVisualNotificationPayload = $ReadOnly<
| {
- ...AndroidNotificationPayloadBase,
+ ...AndroidVisualNotificationPayloadBase,
+messageInfos?: string,
}
| {
- ...AndroidNotificationPayloadBase,
+ ...AndroidVisualNotificationPayloadBase,
+blobHash: string,
+encryptionKey: string,
- };
+ },
+>;
-export type AndroidNotification = {
- +data: {
+export type AndroidVisualNotification = {
+ +data: $ReadOnly<{
+id?: string,
- +badgeOnly?: string,
+keyserverID: string,
- ...AndroidNotificationPayload | { +encryptedPayload: string },
- },
+ ...
+ | {
+ ...AndroidVisualNotificationPayloadBase,
+ +messageInfos?: string,
+ }
+ | {
+ ...AndroidVisualNotificationPayloadBase,
+ +blobHash: string,
+ +encryptionKey: string,
+ }
+ | { +encryptedPayload: string },
+ }>,
};
export type AndroidNotificationRescind = {
- +data: {
+ +data: $ReadOnly<{
+keyserverID: string,
...
| {
@@ -57,14 +68,37 @@
+encryptionFailed?: string,
}
| { +encryptedPayload: string },
- },
+ }>,
};
-export type TargetedAndroidNotification = {
- +notification: AndroidNotification | AndroidNotificationRescind,
+export type AndroidBadgeOnlyNotification = {
+ +data: $ReadOnly<{
+ +keyserverID: string,
+ ...
+ | {
+ +badge: string,
+ +badgeOnly: '1',
+ +encryptionFailed?: string,
+ }
+ | { +encryptedPayload: string },
+ }>,
+};
+
+type AndroidNotificationWithPriority =
+ | {
+ +notification: AndroidVisualNotification,
+ +priority: 'high',
+ }
+ | {
+ +notification: AndroidBadgeOnlyNotification | AndroidNotificationRescind,
+ +priority: 'normal',
+ };
+
+export type TargetedAndroidNotification = $ReadOnly<{
+ ...AndroidNotificationWithPriority,
+deviceToken: string,
+encryptionOrder?: number,
-};
+}>;
export type TargetedWebNotification = {
+notification: WebNotification,
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
@@ -119,9 +119,7 @@
return { success: true };
}
invariant(fcmProvider, `keyserver/secrets/${pushProfile}.json should exist`);
- const options: Object = {
- priority: 'high',
- };
+ const options: Object = {};
if (collapseKey) {
options.collapseKey = collapseKey;
}
@@ -130,9 +128,13 @@
// multicast messages and one of the device tokens is invalid, the resultant
// 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 results = await Promise.all(
- targetedNotifications.map(({ notification, deviceToken }) => {
- return fcmSinglePush(fcmProvider, notification, deviceToken, options);
+ targetedNotifications.map(({ notification, deviceToken, priority }) => {
+ return fcmSinglePush(fcmProvider, notification, deviceToken, {
+ ...options,
+ priority,
+ });
}),
);
diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
@@ -38,6 +38,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
+import java.util.Map;
import me.leolin.shortcutbadger.ShortcutBadger;
import org.json.JSONException;
import org.json.JSONObject;
@@ -57,6 +58,8 @@
private static final String KEYSERVER_ID_KEY = "keyserverID";
private static final String CHANNEL_ID = "default";
private static final long[] VIBRATION_SPEC = {500, 500};
+ private static final Map<Integer, String> NOTIF_PRIORITY_VERBOSE =
+ Map.of(0, "UNKNOWN", 1, "HIGH", 2, "NORMAL");
// Those and future MMKV-related constants should match
// similar constants in NotificationService.mm
@@ -103,7 +106,10 @@
@Override
public void onMessageReceived(RemoteMessage message) {
- if (message.getData().get(KEYSERVER_ID_KEY) == null) {
+ handleAlteredNotificationPriority(message);
+
+ if (StaffUtils.isStaffRelease() &&
+ message.getData().get(KEYSERVER_ID_KEY) == null) {
displayErrorMessageNotification(
"Received notification without keyserver ID.",
"Missing keyserver ID.",
@@ -170,6 +176,32 @@
this.displayNotification(message);
}
+ private void handleAlteredNotificationPriority(RemoteMessage message) {
+ if (!StaffUtils.isStaffRelease()) {
+ return;
+ }
+
+ int originalPriority = message.getOriginalPriority();
+ int priority = message.getPriority();
+
+ String priorityName = NOTIF_PRIORITY_VERBOSE.get(priority);
+ String originalPriorityName = NOTIF_PRIORITY_VERBOSE.get(originalPriority);
+
+ if (priorityName == null || originalPriorityName == null) {
+ // Technically this will never happen as
+ // it would violate FCM documentation
+ return;
+ }
+
+ if (priority != originalPriority) {
+ displayErrorMessageNotification(
+ "System changed notification priority from " + priorityName + " to " +
+ originalPriorityName,
+ "Notification priority altered.",
+ null);
+ }
+ }
+
private boolean isAppInForeground() {
return ProcessLifecycleOwner.get().getLifecycle().getCurrentState() ==
Lifecycle.State.RESUMED;

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 26, 8:58 AM (12 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2704867
Default Alt Text
D11858.diff (17 KB)

Event Timeline