1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.imsserviceentitlement.fcm;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.util.Log;
22 
23 import com.android.imsserviceentitlement.ImsEntitlementPollingService;
24 import com.android.imsserviceentitlement.job.JobManager;
25 import com.android.imsserviceentitlement.utils.TelephonyUtils;
26 import com.android.libraries.entitlement.ServiceEntitlement;
27 
28 import com.google.common.annotations.VisibleForTesting;
29 import com.google.firebase.messaging.FirebaseMessagingService;
30 import com.google.firebase.messaging.RemoteMessage;
31 
32 import java.util.Map;
33 
34 /** Service for handling Firebase Cloud Messaging.*/
35 public class FcmService extends FirebaseMessagingService {
36     private static final String TAG = "IMSSE-FcmService";
37 
38     private static final String DATA_APP_KEY = "app";
39     private static final String DATA_TIMESTAMP_KEY = "timestamp";
40 
41     private JobManager mJobManager;
42 
43     @Override
44     @VisibleForTesting
attachBaseContext(Context base)45     protected void attachBaseContext(Context base) {
46         super.attachBaseContext(base);
47     }
48 
49     /**
50      * Called when a new token for the default Firebase project is generated.
51      *
52      * @param token the token used for sending messages to this application instance.
53      */
54     @Override
onNewToken(String token)55     public void onNewToken(String token) {
56         Log.d(TAG, "New token: " + token);
57 
58         // TODO(b/182560867): check if we need to update the new token to server.
59 
60         // Note we cannot directly save the new token, as we don't know which subId
61         // it's associated with.
62         FcmRegistrationService.enqueueJob(this);
63     }
64 
65     /**
66      * Handles FCM message for entitlement.
67      *
68      * @param message holds the message received from Firebase Cloud Messaging.
69      */
70     @Override
onMessageReceived(RemoteMessage message)71     public void onMessageReceived(RemoteMessage message) {
72         // Not testable.
73         onMessageReceived(message.getSenderId(), message.getData());
74     }
75 
76     @VisibleForTesting
onMessageReceived(String fcmSenderId, Map<String, String> fcmData)77     void onMessageReceived(String fcmSenderId, Map<String, String> fcmData) {
78         Log.d(TAG, "onMessageReceived, SenderId:" + fcmSenderId);
79         if (!isTs43EntitlementsChangeEvent(fcmData)) {
80             Log.i(TAG, "Ignore message not related to entitlements change.");
81             return;
82         }
83         // A corner case: a FCM received after SIM is removed, and SIM inserted back later.
84         // We missed the FCM in this case.
85         scheduleEntitlementStatusCheckForSubIdAssociatedWithSenderId(fcmSenderId);
86     }
87 
isTs43EntitlementsChangeEvent(Map<String, String> dataMap)88     private static boolean isTs43EntitlementsChangeEvent(Map<String, String> dataMap) {
89         if (dataMap == null) {
90             return false;
91         }
92         Log.v(TAG, "The payload data: " + dataMap);
93 
94         // Based on GSMA TS.43 2.4.2 Messaging Infrastructure-Based Notifications, the notification
95         // payload for multiple applications follows:
96         // "data":
97         //  {
98         //    "app": ["ap2003", "ap2004", "ap2005"],
99         //    "timestamp": "2019-01-29T13:15:31-08:00"
100         //  }
101         if (!dataMap.containsKey(DATA_APP_KEY) || !dataMap.containsKey(DATA_TIMESTAMP_KEY)) {
102             Log.d(TAG, "data format error");
103             return false;
104         }
105         // Check if APP_VOWIFI i.e. "ap2004" is in notification data.
106         if (dataMap.get(DATA_APP_KEY).contains(ServiceEntitlement.APP_VOWIFI)) {
107             return true;
108         }
109         return false;
110     }
111 
112     @VisibleForTesting
setMockJobManager(JobManager jobManager)113     void setMockJobManager(JobManager jobManager) {
114         mJobManager = jobManager;
115     }
116 
getJobManager(int subId)117     private JobManager getJobManager(int subId) {
118         return (mJobManager != null)
119                 ? mJobManager
120                 : JobManager.getInstance(
121                         this,
122                         ImsEntitlementPollingService.COMPONENT_NAME,
123                         subId);
124     }
125 
scheduleEntitlementStatusCheckForSubIdAssociatedWithSenderId(String msgSenderId)126     private void scheduleEntitlementStatusCheckForSubIdAssociatedWithSenderId(String msgSenderId) {
127         for (int subId : TelephonyUtils.getSubIdsWithFcmSupported(this)) {
128             String configSenderId = TelephonyUtils.getFcmSenderId(this, subId);
129             if (msgSenderId.equals(configSenderId)) {
130                 Log.d(TAG, "check entitlement status for subscription id(" + subId + ")");
131                 getJobManager(subId).queryEntitlementStatusOnceNetworkReady();
132             }
133         }
134     }
135 }
136