HarmonyOS NEXT has permanently dropped the Android Open Source Project (AOSP) compatibility layer. Consequently, Google Mobile Services (GMS) and Firebase Cloud Messaging (FCM) are unsupported natively. Applications migrating to HarmonyOS that rely on background payloads, silent updates, or push notifications will fail silently if FCM remains in the codebase.
To maintain background communication, developers must implement HarmonyOS Push Kit. As the primary Firebase alternative Huawei provides, Push Kit integrates directly with the HarmonyOS microkernel and power management subsystem to deliver messages without requiring a persistently active application process.
This guide outlines the architectural shift and provides the production-ready code to migrate backend and client services to Huawei Push Kit.
Why FCM Fails on HarmonyOS NEXT
FCM relies on the Google Play Services daemon, a highly privileged background process that maintains persistent TCP/IP sockets with Google's cloud network. On Android, this daemon receives incoming packets and routes them to the corresponding application via intents.
HarmonyOS NEXT utilizes a completely different distributed architecture and aggressive background process management. The OS suspends non-system background sockets to preserve battery life. Because Google Play Services cannot be installed on HarmonyOS NEXT, FCM cannot maintain its network connection.
Instead, HarmonyOS provides its own system-level daemon tied to Huawei Mobile Services (HMS) Core. Push Kit is the only authorized conduit for waking up a dormant application or delivering payloads to the system tray on HarmonyOS NEXT. This makes it a mandatory component for cloud notification services and enterprise app messaging.
Backend Implementation: Authenticating and Dispatching Messages
Unlike FCM's older legacy API which relied on static server keys, Huawei Push Kit enforces a strict OAuth 2.0 client credentials flow. Your backend must first exchange its Client ID and Client Secret for a bearer token before dispatching a push payload.
Step 1: Requesting the Access Token
The following TypeScript code uses modern ES2024 native fetch to retrieve an access token. This token typically lives for 3600 seconds and should be cached in Redis or in-memory to prevent rate-limiting.
// auth.service.ts
interface HuaweiAuthResponse {
access_token: string;
expires_in: number;
token_type: string;
}
export async function getHuaweiAccessToken(clientId: string, clientSecret: string): Promise<string> {
const tokenUrl = 'https://oauth-login.cloud.huawei.com/oauth2/v3/token';
const params = new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
});
const response = await fetch(tokenUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params.toString(),
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(`Huawei OAuth failed: ${response.status} - ${errorBody}`);
}
const data = (await response.json()) as HuaweiAuthResponse;
return data.access_token;
}
Step 2: Dispatching the Push Payload
Once authenticated, you must construct a payload that complies with Huawei's REST API specifications. For enterprise app messaging, separating the visual notification object from the strictly logical data object is critical.
// push.service.ts
interface PushMessagePayload {
validate_only: boolean;
message: {
notification?: {
title: string;
body: string;
};
data?: string; // Must be a serialized JSON string in Huawei Push Kit
token: string[];
};
}
export async function sendHarmonyOsPush(
projectId: string,
accessToken: string,
targetTokens: string[],
title: string,
body: string,
customData: Record<string, unknown>
): Promise<void> {
const pushUrl = `https://push-api.cloud.huawei.com/v1/${projectId}/messages:send`;
const payload: PushMessagePayload = {
validate_only: false,
message: {
notification: {
title,
body,
},
data: JSON.stringify(customData),
token: targetTokens,
},
};
const response = await fetch(pushUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(`Push delivery failed: ${response.status} - ${errorBody}`);
}
}
Client Implementation: ArkTS Token Retrieval and Handling
On the HarmonyOS client, the application must register with the system push service to receive its unique routing token (the equivalent of an FCM registration token).
Retrieving the Push Token
This code should execute within the onCreate lifecycle method of your primary UIAbility. Using ArkTS, we interface with @kit.PushKit to securely request the device token.
// EntryAbility.ets
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { pushService } from '@kit.PushKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
try {
// Retrieve the Push Token asynchronously
const pushToken: string = await pushService.getToken();
hilog.info(0x0000, 'PushModule', `Successfully retrieved Push Token: ${pushToken}`);
// Implement your custom logic to sync this token to your backend API
await this.syncTokenToBackend(pushToken);
} catch (err) {
const error = err as BusinessError;
hilog.error(0x0000, 'PushModule', `Failed to get Push Token: Code ${error.code}, Message ${error.message}`);
}
}
private async syncTokenToBackend(token: string): Promise<void> {
// Standard fetch implementation to your backend server
}
}
Deep Dive: Payload Routing and Execution Environments
When the backend fires a request to the Huawei Push server, the routing differs based on the payload structure:
- Notification Messages: If the
notificationblock is present, the HarmonyOS System UI interceptor catches the message. It bypasses the app entirely and renders the notification directly in the system tray. This guarantees delivery even if the app process is entirely killed. - Data Messages: If only the
datablock is present, the OS wakes the application temporarily in a restricted background execution environment. The app is granted a brief CPU window (typically 10 seconds) to process the raw JSON, decrypt local data, or synchronize database states before the OS suspends the process again.
This separation of concerns is why migrating from FCM requires strict attention to the JSON payload structure. Mixing these payload types improperly can result in the OS suppressing the background execution window.
Common Pitfalls and Edge Cases
1. Token Expiration and AAID Rotation
Unlike static device IDs, Push Kit uses an Anonymous Application Identifier (AAID). The push token is derived from this AAID. If the user clears the app data, reinstalls the app, or restores the device from a backup, the token is invalidated. Always listen for the onNewToken event (if using legacy broadcast receivers) or routinely verify the token string on application startup as demonstrated in the ArkTS code above.
2. Cross-Region Data Routing
Huawei Push Kit operates under strict data residency laws. If your HarmonyOS application is deployed globally, your backend must route the push request to the correct geographical endpoint (e.g., Germany for the EU, Singapore for Asia). Sending a token generated by a European device to the Chinese push endpoint will result in an Illegal Token response.
3. Payload Size Limitations
Enterprise cloud notification services often embed heavily serialized data. HarmonyOS limits data payloads to 4KB. Exceeding this limit results in a 413 Payload Too Large error. To circumvent this, send a lightweight data payload containing an ID or timestamp, and have the application utilize its background execution window to perform a standard HTTPS GET request to retrieve the full data object.