1 /*
2  * Copyright (C) 2022 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.cellbroadcastservice;
18 
19 import static android.content.Context.MODE_PRIVATE;
20 import static android.telephony.SmsCbMessage.MESSAGE_FORMAT_3GPP;
21 
22 import static com.android.cellbroadcastservice.CellBroadcastMetrics.FILTER_CDMA;
23 import static com.android.cellbroadcastservice.CellBroadcastMetrics.FILTER_GSM;
24 
25 import android.content.Context;
26 import android.content.SharedPreferences;
27 import android.telephony.SmsCbMessage;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * CellBroadcastServiceMetrics
38  * Logging featureUpdated, when alert message is received or channel range is updated
39  */
40 public class CellBroadcastServiceMetrics {
41 
42     private static final String TAG = "CellBroadcastServiceMetrics";
43     private static final boolean VDBG = false;
44     // Key to access the shared preference of cell broadcast service feature for metric.
45     private static final String CBS_METRIC_PREF = "CellBroadcastServiceMetricSharedPref";
46 
47     private static CellBroadcastServiceMetrics sCbsMetrics;
48 
49     private FeatureMetrics mFeatureMetrics;
50     private FeatureMetrics mFeatureMetricsSharedPreferences;
51 
52 
53     /**
54      * Get instance of CellBroadcastServiceMetrics.
55      */
getInstance()56     public static CellBroadcastServiceMetrics getInstance() {
57         if (sCbsMetrics == null) {
58             sCbsMetrics = new CellBroadcastServiceMetrics();
59         }
60         return sCbsMetrics;
61     }
62 
63     /**
64      * CellBroadcastReceiverMetrics.FeatureMetrics
65      * Logging featureUpdated as needed when alert message is received
66      */
67     public static class FeatureMetrics implements Cloneable {
68         public static final String ADDITIONAL_CBR_PACKAGES = "additional_cbr_packages";
69         public static final String AREA_INFO_PACKAGES = "area_info_packages";
70         public static final String RESET_AREA_INFO = "reset_area_info";
71         public static final String DEFVAL_AREAPKGS = "com.android.settings";
72 
73         private boolean mIsOverrideCbrPkgs;
74         private boolean mIsOverrideAreaInfoPkgs;
75         private boolean mResetAreaInfo;
76 
77         private Context mContext;
78 
FeatureMetrics(Context context)79         public FeatureMetrics(Context context) {
80             mContext = context;
81             SharedPreferences sp = mContext.getSharedPreferences(CBS_METRIC_PREF, MODE_PRIVATE);
82 
83             mIsOverrideCbrPkgs = sp.getBoolean(ADDITIONAL_CBR_PACKAGES, false);
84             mIsOverrideAreaInfoPkgs = sp.getBoolean(AREA_INFO_PACKAGES, false);
85             mResetAreaInfo = sp.getBoolean(RESET_AREA_INFO, false);
86         }
87 
88         @Override
hashCode()89         public int hashCode() {
90             return Objects.hash(mIsOverrideCbrPkgs, mIsOverrideAreaInfoPkgs, mResetAreaInfo);
91         }
92 
93         @Override
equals(Object object)94         public boolean equals(Object object) {
95             if (object instanceof FeatureMetrics) {
96                 FeatureMetrics features = (FeatureMetrics) object;
97                 return (this.mIsOverrideCbrPkgs == features.mIsOverrideCbrPkgs
98                         && this.mIsOverrideAreaInfoPkgs == features.mIsOverrideAreaInfoPkgs
99                         && this.mResetAreaInfo == features.mResetAreaInfo);
100             }
101             return false;
102         }
103 
104         @Override
clone()105         public Object clone() throws CloneNotSupportedException {
106             return super.clone();
107         }
108 
109         /**
110          * Get current status whether cell broadcast receiver packages are overridden
111          */
112         @VisibleForTesting
isOverrideCbrPkgs()113         public boolean isOverrideCbrPkgs() {
114             return mIsOverrideCbrPkgs;
115         }
116 
117         /**
118          * Get current status whether area information packages are overridden
119          */
120         @VisibleForTesting
isOverrideAreaInfoPkgs()121         public boolean isOverrideAreaInfoPkgs() {
122             return mIsOverrideAreaInfoPkgs;
123         }
124 
125         /**
126          * Get current status whether reset area information while in out of service
127          */
128         @VisibleForTesting
isResetAreaInfo()129         public boolean isResetAreaInfo() {
130             return mResetAreaInfo;
131         }
132 
133         /**
134          * Set whether additional cbr packages are overridden
135          *
136          * @param override : whether additional cbr packages are overridden
137          */
138         @VisibleForTesting
onChangedAdditionalCbrPackage(boolean override)139         public void onChangedAdditionalCbrPackage(boolean override) {
140             mIsOverrideCbrPkgs = override;
141         }
142 
143         /**
144          * Set whether area info packages are overridden
145          *
146          * @param current : list of area info overriding packages
147          */
148         @VisibleForTesting
onChangedAreaInfoPackage(List<String> current)149         public void onChangedAreaInfoPackage(List<String> current) {
150             mIsOverrideAreaInfoPkgs = !Arrays.asList(new String[]{DEFVAL_AREAPKGS}).equals(current);
151         }
152 
153         /**
154          * Set whether area info reset on our of service
155          *
156          * @param current : whether reset area info is supported
157          */
158         @VisibleForTesting
onChangedResetAreaInfo(boolean current)159         public void onChangedResetAreaInfo(boolean current) {
160             mResetAreaInfo = current;
161         }
162 
163         /**
164          * Calling check-in method for CB_SERVICE_FEATURE
165          */
166         @VisibleForTesting
logFeatureChanged()167         public void logFeatureChanged() {
168             CellBroadcastModuleStatsLog.write(
169                     CellBroadcastModuleStatsLog.CB_SERVICE_FEATURE_CHANGED,
170                     mIsOverrideCbrPkgs,
171                     mIsOverrideAreaInfoPkgs,
172                     mResetAreaInfo);
173             if (VDBG) Log.d(TAG, this.toString());
174         }
175 
176         /**
177          * Update preferences for service feature metrics
178          */
179         @VisibleForTesting
updateSharedPreferences()180         public void updateSharedPreferences() {
181             SharedPreferences sp = mContext.getSharedPreferences(CBS_METRIC_PREF, MODE_PRIVATE);
182             SharedPreferences.Editor editor = sp.edit();
183             editor.putBoolean(ADDITIONAL_CBR_PACKAGES, mIsOverrideCbrPkgs);
184             editor.putBoolean(AREA_INFO_PACKAGES, mIsOverrideAreaInfoPkgs);
185             editor.putBoolean(RESET_AREA_INFO, mResetAreaInfo);
186             editor.apply();
187         }
188 
189         @Override
toString()190         public String toString() {
191             return "CellBroadcast_Service_Feature : "
192                     + "mIsOverrideCbrPkgs = " + mIsOverrideCbrPkgs + " | "
193                     + "mIsOverrideAreaInfoPkgs = " + mIsOverrideAreaInfoPkgs + " | "
194                     + "mResetAreaInfo = " + mResetAreaInfo;
195         }
196     }
197 
198     /**
199      * get cached feature metrics for shared preferences
200      */
201     @VisibleForTesting
getFeatureMetricsSharedPreferences()202     public FeatureMetrics getFeatureMetricsSharedPreferences() {
203         return mFeatureMetricsSharedPreferences;
204     }
205 
206     /**
207      * set cached feature metrics for current status
208      */
209     @VisibleForTesting
setFeatureMetrics(FeatureMetrics featureMetrics)210     public void setFeatureMetrics(FeatureMetrics featureMetrics) {
211         mFeatureMetrics = featureMetrics;
212     }
213 
214     /**
215      * Set featureMetricsSharedPreferences
216      *
217      * @param featureMetricsSharedPreferences : Cbs features information
218      */
219     @VisibleForTesting
setFeatureMetricsSharedPreferences(FeatureMetrics featureMetricsSharedPreferences)220     public void setFeatureMetricsSharedPreferences(FeatureMetrics featureMetricsSharedPreferences) {
221         mFeatureMetricsSharedPreferences = featureMetricsSharedPreferences;
222     }
223 
224     /**
225      * Get featureMetrics if null then create
226      */
227     @VisibleForTesting
getFeatureMetrics(Context context)228     public FeatureMetrics getFeatureMetrics(Context context) {
229         if (mFeatureMetrics == null) {
230             mFeatureMetrics = new FeatureMetrics(context);
231             mFeatureMetricsSharedPreferences = new FeatureMetrics(context);
232         }
233         return mFeatureMetrics;
234     }
235 
236     /**
237      * When feature changed and net alert message received then check-in logging
238      *
239      * @param context : Context
240      */
241     @VisibleForTesting
logFeatureChangedAsNeeded(Context context)242     public void logFeatureChangedAsNeeded(Context context) {
243         if (!getFeatureMetrics(context).equals(mFeatureMetricsSharedPreferences)) {
244             mFeatureMetrics.logFeatureChanged();
245             mFeatureMetrics.updateSharedPreferences();
246             try {
247                 mFeatureMetricsSharedPreferences = (FeatureMetrics) mFeatureMetrics.clone();
248             } catch (CloneNotSupportedException e) {
249                 Log.e(TAG, "exception during making clone for service feature metrics:  " + e);
250             }
251         }
252     }
253 
254     /**
255      * Create a new logMessageReported
256      *
257      * @param type     : radio type
258      * @param source   : layer of reported message
259      * @param serialNo : unique identifier of message
260      * @param msgId    : service_category of message
261      */
logMessageReported(Context context, int type, int source, int serialNo, int msgId)262     public void logMessageReported(Context context, int type, int source, int serialNo, int msgId) {
263         if (VDBG) {
264             Log.d(TAG,
265                     "logMessageReported : " + type + " " + source + " " + serialNo + " " + msgId);
266         }
267         CellBroadcastModuleStatsLog.write(CellBroadcastModuleStatsLog.CB_MESSAGE_REPORTED, type,
268                 source, serialNo, msgId);
269     }
270 
271     /**
272      * Create a new logMessageError
273      *
274      * @param type             : error type
275      * @param exceptionMessage : error message
276      */
logMessageError(int type, String exceptionMessage)277     public void logMessageError(int type, String exceptionMessage) {
278         if (VDBG) {
279             Log.d(TAG, "logMessageError : " + type + " " + exceptionMessage);
280         }
281         CellBroadcastModuleStatsLog.write(CellBroadcastModuleStatsLog.CB_MESSAGE_ERROR,
282                 type, exceptionMessage);
283     }
284 
285     /**
286      * Create a new logMessageFiltered
287      *
288      * @param filterType : reason type of filtered
289      * @param msg        : sms cell broadcast message information
290      */
logMessageFiltered(int filterType, SmsCbMessage msg)291     public void logMessageFiltered(int filterType, SmsCbMessage msg) {
292         int ratType = msg.getMessageFormat() == MESSAGE_FORMAT_3GPP ? FILTER_GSM : FILTER_CDMA;
293         if (VDBG) {
294             Log.d(TAG, "logMessageFiltered : " + ratType + " " + filterType + " "
295                     + msg.getSerialNumber() + " " + msg.getServiceCategory());
296         }
297         CellBroadcastModuleStatsLog.write(CellBroadcastModuleStatsLog.CB_MESSAGE_FILTERED,
298                 ratType, filterType, msg.getSerialNumber(), msg.getServiceCategory());
299     }
300 
301     /**
302      * Create a new logModuleError
303      *
304      * @param source    : where this log happened
305      * @param errorType : type of error
306      */
logModuleError(int source, int errorType)307     public void logModuleError(int source, int errorType) {
308         if (VDBG) {
309             Log.d(TAG, "logModuleError : " + source + " " + errorType);
310         }
311         CellBroadcastModuleStatsLog.write(CellBroadcastModuleStatsLog.CB_MODULE_ERROR_REPORTED,
312                 source, errorType);
313     }
314 }
315