1 /*
2  * Copyright (C) 2023 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.server.backup.utils;
18 
19 import android.app.backup.BackupAnnotations;
20 import android.app.backup.BackupManagerMonitor;
21 import android.app.backup.BackupRestoreEventLogger;
22 import android.os.Bundle;
23 import android.os.Environment;
24 import android.util.Slog;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.internal.util.FastPrintWriter;
28 
29 import java.io.BufferedReader;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.FileOutputStream;
33 import java.io.FileWriter;
34 import java.io.IOException;
35 import java.io.InputStreamReader;
36 import java.io.PrintWriter;
37 import java.text.SimpleDateFormat;
38 import java.util.ArrayList;
39 import java.util.Date;
40 import java.util.Map;
41 import java.util.concurrent.TimeUnit;
42 
43 
44 /*
45  * Util class to parse a BMM event and write it to a text file, to be the printed in
46  * the backup dumpsys
47  *
48  * Note: this class is note thread safe
49  */
50 public class BackupManagerMonitorDumpsysUtils {
51 
52     private static final String TAG = "BackupManagerMonitorDumpsysUtils";
53     // Name of the subdirectory where the text file containing the BMM events will be stored.
54     // Same as {@link UserBackupManagerFiles}
55     private static final String BACKUP_PERSISTENT_DIR = "backup";
56     private static final String INITIAL_SETUP_TIMESTAMP_KEY = "initialSetupTimestamp";
57     // Retention period of 60 days (in millisec) for the BMM Events.
58     // After tha time has passed the text file containing the BMM events will be emptied
59     private static final long LOGS_RETENTION_PERIOD_MILLISEC = 60 * TimeUnit.DAYS.toMillis(1);
60     // Size limit for the text file containing the BMM events
61     private static final long BMM_FILE_SIZE_LIMIT_BYTES = 25 * 1024 * 1000; // 2.5 MB
62 
63     // We cache the value of IsAfterRetentionPeriod() to avoid unnecessary disk I/O
64     // mIsAfterRetentionPeriodCached tracks if we have cached the value of IsAfterRetentionPeriod()
65     private boolean mIsAfterRetentionPeriodCached = false;
66     // The cached value of IsAfterRetentionPeriod()
67     private boolean mIsAfterRetentionPeriod;
68     // If isFileLargerThanSizeLimit(bmmEvents)  returns true we cache the value to avoid
69     // unnecessary disk I/O
70    private boolean mIsFileLargerThanSizeLimit = false;
71 
72     /**
73      * Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that
74      * will be persisted in a text file and printed in the dumpsys.
75      *
76      * If the eventBundle passed is not a RESTORE event, return early
77      *
78      * Key information related to the event:
79      * - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT)
80      * - Event ID
81      * - Event Category
82      * - Operation type
83      * - Package name (can be null)
84      * - Agent logs (if available)
85      *
86      * Example of formatting:
87      * [2023-09-21 14:43:33.824] - Agent logging results
88      *   Package: com.android.wallpaperbackup
89      *   Agent Logs:
90      *           Data Type: wlp_img_system
91      *                   Item restored: 0/1
92      *                   Agent Error - Category: no_wallpaper, Count: 1
93      *           Data Type: wlp_img_lock
94      *                   Item restored: 0/1
95      *                   Agent Error - Category: no_wallpaper, Count: 1
96      */
parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle)97     public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) {
98         if (isAfterRetentionPeriod()) {
99             // We only log data for the first 60 days since setup
100             return;
101         }
102 
103         if (eventBundle == null) {
104             return;
105         }
106 
107         if (!isOpTypeRestore(eventBundle)) {
108             //We only log Restore events
109             return;
110         }
111 
112         if (!eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_ID)
113                 || !eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY)) {
114             Slog.w(TAG, "Event id and category are not optional fields.");
115             return;
116         }
117         File bmmEvents = getBMMEventsFile();
118 
119         if (bmmEvents.length() == 0) {
120             // We are parsing the first restore event.
121             // Time to also record the setup timestamp of the device
122             recordSetUpTimestamp();
123         }
124 
125         if(isFileLargerThanSizeLimit(bmmEvents)){
126             // Do not write more events if the file is over size limit
127             return;
128         }
129 
130         try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true);
131              PrintWriter pw = new FastPrintWriter(out);) {
132 
133             int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
134             int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
135 
136             if (eventId == BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS &&
137                     !hasAgentLogging(eventBundle)) {
138                 // Do not record an empty agent logging event
139                 return;
140             }
141 
142             pw.println("[" + timestamp() + "] - " + getId(eventId));
143 
144             if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) {
145                 pw.println("\tPackage: "
146                         + eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
147             }
148 
149             // TODO(b/296818666): add extras to the events
150             addAgentLogsIfAvailable(eventBundle, pw);
151             addExtrasIfAvailable(eventBundle, pw);
152         } catch (java.io.IOException e) {
153             Slog.e(TAG, "IO Exception when writing BMM events to file: " + e);
154         }
155 
156     }
157 
hasAgentLogging(Bundle eventBundle)158     private boolean hasAgentLogging(Bundle eventBundle) {
159         if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) {
160             ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
161                     eventBundle.getParcelableArrayList(
162                             BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
163 
164             return !agentLogs.isEmpty();
165         }
166         return false;
167     }
168 
169     /**
170      * Extracts agent logs from the BackupManagerMonitor event. These logs detail:
171      * - the data type for the agent
172      * - the count of successfully restored items
173      * - the count of items that failed to restore
174      * - the metadata associated with this datatype
175      * - any errors
176      */
addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw)177     private void addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw) {
178         if (hasAgentLogging(eventBundle)) {
179             pw.println("\tAgent Logs:");
180             ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
181                     eventBundle.getParcelableArrayList(
182                             BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
183             for (BackupRestoreEventLogger.DataTypeResult result : agentLogs) {
184                 int totalItems = result.getFailCount() + result.getSuccessCount();
185                 pw.println("\t\tData Type: " + result.getDataType());
186                 pw.println("\t\t\tItem restored: " + result.getSuccessCount() + "/" +
187                         totalItems);
188                 for (Map.Entry<String, Integer> entry : result.getErrors().entrySet()) {
189                     pw.println("\t\t\tAgent Error - Category: " +
190                             entry.getKey() + ", Count: " + entry.getValue());
191                 }
192             }
193         }
194     }
195 
196     /**
197      * Extracts some extras (defined in BackupManagerMonitor as EXTRA_LOG_<description>)
198      * from the BackupManagerMonitor event. Not all extras have the same importance. For now only
199      * focus on extras relating to version mismatches between packages on the source and target.
200      *
201      * When an event with ID LOG_EVENT_ID_RESTORE_VERSION_HIGHER (trying to restore from higher to
202      * lower version of a package) parse:
203      * EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source
204      * EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version
205      * EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target
206      */
addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw)207     private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) {
208         if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) ==
209                 BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER) {
210             if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)) {
211                 pw.println("\t\tPackage supports RestoreAnyVersion: "
212                         + eventBundle.getBoolean(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY));
213             }
214             if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)) {
215                 pw.println("\t\tPackage version on source: "
216                         + eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION));
217             }
218             if (eventBundle.containsKey(
219                       BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
220                 pw.println("\t\tPackage version on target: "
221                         + eventBundle.getLong(
222                         BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION));
223             }
224         }
225     }
226 
227     /*
228      * Get the path of the text files which stores the BMM events
229      */
getBMMEventsFile()230     public File getBMMEventsFile() {
231         File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
232         File fname = new File(dataDir, "bmmevents.txt");
233         return fname;
234     }
235 
isFileLargerThanSizeLimit(File events)236     public boolean isFileLargerThanSizeLimit(File events){
237         if (!mIsFileLargerThanSizeLimit) {
238             mIsFileLargerThanSizeLimit = events.length() > getBMMEventsFileSizeLimit();
239         }
240         return mIsFileLargerThanSizeLimit;
241     }
242 
timestamp()243     private String timestamp() {
244         long currentTime = System.currentTimeMillis();
245         Date date = new Date(currentTime);
246         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
247         return dateFormat.format(date);
248     }
249 
getCategory(int code)250     private String getCategory(int code) {
251         String category = switch (code) {
252             case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT -> "Transport";
253             case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT -> "Agent";
254             case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY ->
255                     "Backup Manager Policy";
256             default -> "Unknown category code: " + code;
257         };
258         return category;
259     }
260 
getId(int code)261     private String getId(int code) {
262         String id = switch (code) {
263             case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL -> "Full backup cancel";
264             case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY -> "Illegal key";
265             case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND -> "No data to send";
266             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE -> "Package ineligible";
267             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT ->
268                     "Package key-value participant";
269             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED -> "Package stopped";
270             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND -> "Package not found";
271             case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED -> "Backup disabled";
272             case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED ->
273                     "Device not provisioned";
274             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT ->
275                     "Package transport not present";
276             case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT -> "Error preflight";
277             case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT -> "Quota hit preflight";
278             case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP -> "Exception full backup";
279             case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL ->
280                     "Key-value backup cancel";
281             case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE ->
282                     "No restore metadata available";
283             case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED ->
284                     "No PM metadata received";
285             case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA ->
286                     "PM agent has no metadata";
287             case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT -> "Lost transport";
288             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT -> "Package not present";
289             case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER ->
290                     "Restore version higher";
291             case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT -> "App has no agent";
292             case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH -> "Signature mismatch";
293             case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT -> "Can't find agent";
294             case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT ->
295                     "Key-value restore timeout";
296             case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION -> "Restore any version";
297             case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH -> "Versions match";
298             case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER ->
299                     "Version of backup older";
300             case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH ->
301                     "Full restore signature mismatch";
302             case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT -> "System app no agent";
303             case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE ->
304                     "Full restore allow backup false";
305             case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED -> "APK not installed";
306             case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK ->
307                     "Cannot restore without APK";
308             case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE -> "Missing signature";
309             case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE ->
310                     "Expected different package";
311             case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION -> "Unknown version";
312             case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT -> "Full restore timeout";
313             case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST -> "Corrupt manifest";
314             case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH ->
315                     "Widget metadata mismatch";
316             case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION ->
317                     "Widget unknown version";
318             case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES -> "No packages";
319             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL -> "Transport is null";
320             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED ->
321                     "Transport non-incremental backup required";
322             case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results";
323             case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE -> "Start system restore";
324             case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL ->
325                     "Start restore at install";
326             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE ->
327                     "Transport error during start restore";
328             case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME ->
329                     "Cannot get next package name";
330             case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE -> "Unknown restore type";
331             case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE -> "KV restore";
332             case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE -> "Full restore";
333             case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET ->
334                     "No next restore target";
335             case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR -> "KV agent error";
336             case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED ->
337                     "Package restore finished";
338             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE ->
339                     "Transport error KV restore";
340             case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD -> "No feeder thread";
341             case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR -> "Full agent error";
342             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE ->
343                     "Transport error full restore";
344             case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete";
345             case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE ->
346                     "Start package restore";
347             case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE ->
348                     "Agent failure";
349             default -> "Unknown log event ID: " + code;
350         };
351         return id;
352     }
353 
isOpTypeRestore(Bundle eventBundle)354     private boolean isOpTypeRestore(Bundle eventBundle) {
355         return switch (eventBundle.getInt(
356                 BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, -1)) {
357             case BackupAnnotations.OperationType.RESTORE -> true;
358             default -> false;
359         };
360     }
361 
362     /**
363      * Store the timestamp when the device was set up (date when the first BMM event is parsed)
364      * in a text file.
365      */
366     @VisibleForTesting
recordSetUpTimestamp()367     void recordSetUpTimestamp() {
368         File setupDateFile = getSetUpDateFile();
369         // record setup timestamp only once
370         if (setupDateFile.length() == 0) {
371             try (FileOutputStream out = new FileOutputStream(setupDateFile, /*append*/ true);
372                  PrintWriter pw = new FastPrintWriter(out);) {
373                 long currentDate = System.currentTimeMillis();
374                 pw.println(currentDate);
375             } catch (IOException e) {
376                 Slog.w(TAG, "An error occurred while recording the setup date: "
377                         + e.getMessage());
378             }
379         }
380 
381     }
382 
383     @VisibleForTesting
getSetUpDate()384     String getSetUpDate() {
385         File fname = getSetUpDateFile();
386         try (FileInputStream inputStream = new FileInputStream(fname);
387              InputStreamReader reader = new InputStreamReader(inputStream);
388              BufferedReader bufferedReader = new BufferedReader(reader);) {
389             return bufferedReader.readLine();
390         } catch (Exception e) {
391             Slog.w(TAG, "An error occurred while reading the date: " + e.getMessage());
392             return "Could not retrieve setup date";
393         }
394     }
395 
396     @VisibleForTesting
isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec)397     static boolean isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec) {
398         if (startTimeStamp > endTimeStamp) {
399             // Something has gone wrong, timeStamp1 should always precede timeStamp2.
400             // Out of caution return true: we would delete the logs rather than
401             // risking them being kept for longer than the retention period
402             return true;
403         }
404         long timeDifferenceMillis = endTimeStamp - startTimeStamp;
405         return (timeDifferenceMillis >= millisec);
406     }
407 
408     /**
409      * Check if current date is after retention period
410      */
411     @VisibleForTesting
isAfterRetentionPeriod()412     boolean isAfterRetentionPeriod() {
413         if (mIsAfterRetentionPeriodCached) {
414             return mIsAfterRetentionPeriod;
415         } else {
416             File setUpDateFile = getSetUpDateFile();
417             if (setUpDateFile.length() == 0) {
418                 // We are yet to record a setup date. This means we haven't parsed the first event.
419                 mIsAfterRetentionPeriod = false;
420                 mIsAfterRetentionPeriodCached = true;
421                 return false;
422             }
423             try {
424                 long setupTimestamp = Long.parseLong(getSetUpDate());
425                 long currentTimestamp = System.currentTimeMillis();
426                 mIsAfterRetentionPeriod = isDateAfterNMillisec(setupTimestamp, currentTimestamp,
427                         getRetentionPeriodInMillisec());
428                 mIsAfterRetentionPeriodCached = true;
429                 return mIsAfterRetentionPeriod;
430             } catch (NumberFormatException e) {
431                 // An error occurred when parsing the setup timestamp.
432                 // Out of caution return true: we would delete the logs rather than
433                 // risking them being kept for longer than the retention period
434                 mIsAfterRetentionPeriod = true;
435                 mIsAfterRetentionPeriodCached = true;
436                 return true;
437             }
438         }
439     }
440 
441     @VisibleForTesting
getSetUpDateFile()442     File getSetUpDateFile() {
443         File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
444         File setupDateFile = new File(dataDir, INITIAL_SETUP_TIMESTAMP_KEY + ".txt");
445         return setupDateFile;
446     }
447 
448     @VisibleForTesting
getRetentionPeriodInMillisec()449     long getRetentionPeriodInMillisec() {
450         return LOGS_RETENTION_PERIOD_MILLISEC;
451     }
452 
453     @VisibleForTesting
getBMMEventsFileSizeLimit()454     long getBMMEventsFileSizeLimit(){
455         return BMM_FILE_SIZE_LIMIT_BYTES;
456     }
457 
458     /**
459      * Delete the BMM Events file after the retention period has passed.
460      *
461      * @return true if the retention period has passed false otherwise.
462      * we want to return true even if we were unable to delete the file, as this will prevent
463      * expired BMM events from being printed to the dumpsys
464      */
deleteExpiredBMMEvents()465     public boolean deleteExpiredBMMEvents() {
466         try {
467             if (isAfterRetentionPeriod()) {
468                 File bmmEvents = getBMMEventsFile();
469                 if (bmmEvents.exists()) {
470                     if (bmmEvents.delete()) {
471                         Slog.i(TAG, "Deleted expired BMM Events");
472                     } else {
473                         Slog.e(TAG, "Unable to delete expired BMM Events");
474                     }
475                 }
476                 return true;
477             }
478             return false;
479         } catch (Exception e) {
480             // Handle any unexpected exceptions
481             // To be safe we return true as we want to avoid exposing expired BMMEvents
482             return true;
483         }
484     }
485 }
486