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 addAgentLogsIfAvailable(eventBundle, pw); 150 addExtrasIfAvailable(eventBundle, pw); 151 } catch (java.io.IOException e) { 152 Slog.e(TAG, "IO Exception when writing BMM events to file: " + e); 153 } 154 155 } 156 hasAgentLogging(Bundle eventBundle)157 private boolean hasAgentLogging(Bundle eventBundle) { 158 if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) { 159 ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs = 160 eventBundle.getParcelableArrayList( 161 BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS); 162 163 return !agentLogs.isEmpty(); 164 } 165 return false; 166 } 167 168 /** 169 * Extracts agent logs from the BackupManagerMonitor event. These logs detail: 170 * - the data type for the agent 171 * - the count of successfully restored items 172 * - the count of items that failed to restore 173 * - the metadata associated with this datatype 174 * - any errors 175 */ addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw)176 private void addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw) { 177 if (hasAgentLogging(eventBundle)) { 178 pw.println("\tAgent Logs:"); 179 ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs = 180 eventBundle.getParcelableArrayList( 181 BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS); 182 for (BackupRestoreEventLogger.DataTypeResult result : agentLogs) { 183 int totalItems = result.getFailCount() + result.getSuccessCount(); 184 pw.println("\t\tData Type: " + result.getDataType()); 185 pw.println("\t\t\tItem restored: " + result.getSuccessCount() + "/" + 186 totalItems); 187 for (Map.Entry<String, Integer> entry : result.getErrors().entrySet()) { 188 pw.println("\t\t\tAgent Error - Category: " + 189 entry.getKey() + ", Count: " + entry.getValue()); 190 } 191 } 192 } 193 } 194 195 /** 196 * Extracts some extras (defined in BackupManagerMonitor as EXTRA_LOG_<description>) 197 * from the BackupManagerMonitor event. Not all extras have the same importance. For now only 198 * focus on extras relating to version mismatches between packages on the source and target. 199 * 200 * When an event with ID LOG_EVENT_ID_RESTORE_VERSION_HIGHER (trying to restore from higher to 201 * lower version of a package) parse: 202 * EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source 203 * EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version 204 * EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target 205 * 206 * When we are performing a V to U downgrade (event with id V_TO_U_RESTORE_SET_LIST) we record 207 * the value of the V to U allowlist and denylist: 208 * EXTRA_LOG_V_TO_U_ALLOWLIST[string] 209 * EXTRA_LOG_V_TO_U_DENYLIST[string] 210 */ addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw)211 private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) { 212 if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) == 213 BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER) { 214 if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)) { 215 pw.println("\t\tPackage supports RestoreAnyVersion: " 216 + eventBundle.getBoolean(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)); 217 } 218 if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)) { 219 pw.println("\t\tPackage version on source: " 220 + eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)); 221 } 222 if (eventBundle.containsKey( 223 BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) { 224 pw.println("\t\tPackage version on target: " 225 + eventBundle.getLong( 226 BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)); 227 } 228 } 229 230 if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) 231 == BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST) { 232 if (eventBundle.containsKey( 233 BackupManagerMonitor.EXTRA_LOG_V_TO_U_DENYLIST)) { 234 pw.println("\t\tV to U Denylist : " 235 + eventBundle.getString( 236 BackupManagerMonitor.EXTRA_LOG_V_TO_U_DENYLIST)); 237 } 238 239 if (eventBundle.containsKey( 240 BackupManagerMonitor.EXTRA_LOG_V_TO_U_ALLOWLIST)) { 241 pw.println("\t\tV to U Allowllist : " 242 + eventBundle.getString( 243 BackupManagerMonitor.EXTRA_LOG_V_TO_U_ALLOWLIST)); 244 } 245 } 246 } 247 248 /* 249 * Get the path of the text files which stores the BMM events 250 */ getBMMEventsFile()251 public File getBMMEventsFile() { 252 File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR); 253 File fname = new File(dataDir, "bmmevents.txt"); 254 return fname; 255 } 256 isFileLargerThanSizeLimit(File events)257 public boolean isFileLargerThanSizeLimit(File events){ 258 if (!mIsFileLargerThanSizeLimit) { 259 mIsFileLargerThanSizeLimit = events.length() > getBMMEventsFileSizeLimit(); 260 } 261 return mIsFileLargerThanSizeLimit; 262 } 263 timestamp()264 private String timestamp() { 265 long currentTime = System.currentTimeMillis(); 266 Date date = new Date(currentTime); 267 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 268 return dateFormat.format(date); 269 } 270 getCategory(int code)271 private String getCategory(int code) { 272 String category = switch (code) { 273 case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT -> "Transport"; 274 case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT -> "Agent"; 275 case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY -> 276 "Backup Manager Policy"; 277 default -> "Unknown category code: " + code; 278 }; 279 return category; 280 } 281 getId(int code)282 private String getId(int code) { 283 String id = switch (code) { 284 case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL -> "Full backup cancel"; 285 case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY -> "Illegal key"; 286 case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND -> "No data to send"; 287 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE -> "Package ineligible"; 288 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT -> 289 "Package key-value participant"; 290 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED -> "Package stopped"; 291 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND -> "Package not found"; 292 case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED -> "Backup disabled"; 293 case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED -> 294 "Device not provisioned"; 295 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT -> 296 "Package transport not present"; 297 case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT -> "Error preflight"; 298 case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT -> "Quota hit preflight"; 299 case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP -> "Exception full backup"; 300 case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL -> 301 "Key-value backup cancel"; 302 case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE -> 303 "No restore metadata available"; 304 case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED -> 305 "No PM metadata received"; 306 case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA -> 307 "PM agent has no metadata"; 308 case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT -> "Lost transport"; 309 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT -> "Package not present"; 310 case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER -> 311 "Restore version higher"; 312 case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT -> "App has no agent"; 313 case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH -> "Signature mismatch"; 314 case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT -> "Can't find agent"; 315 case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT -> 316 "Key-value restore timeout"; 317 case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION -> "Restore any version"; 318 case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH -> "Versions match"; 319 case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER -> 320 "Version of backup older"; 321 case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH -> 322 "Full restore signature mismatch"; 323 case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT -> "System app no agent"; 324 case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE -> 325 "Full restore allow backup false"; 326 case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED -> "APK not installed"; 327 case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK -> 328 "Cannot restore without APK"; 329 case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE -> "Missing signature"; 330 case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE -> 331 "Expected different package"; 332 case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION -> "Unknown version"; 333 case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT -> "Full restore timeout"; 334 case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST -> "Corrupt manifest"; 335 case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH -> 336 "Widget metadata mismatch"; 337 case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION -> 338 "Widget unknown version"; 339 case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES -> "No packages"; 340 case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL -> "Transport is null"; 341 case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED -> 342 "Transport non-incremental backup required"; 343 case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results"; 344 case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE -> "Start system restore"; 345 case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL -> 346 "Start restore at install"; 347 case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE -> 348 "Transport error during start restore"; 349 case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME -> 350 "Cannot get next package name"; 351 case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE -> "Unknown restore type"; 352 case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE -> "KV restore"; 353 case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE -> "Full restore"; 354 case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET -> 355 "No next restore target"; 356 case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR -> "KV agent error"; 357 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED -> 358 "Package restore finished"; 359 case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE -> 360 "Transport error KV restore"; 361 case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD -> "No feeder thread"; 362 case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR -> "Full agent error"; 363 case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE -> 364 "Transport error full restore"; 365 case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete"; 366 case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE -> "Start package restore"; 367 case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE -> "Agent failure"; 368 case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_ELIGIBLE -> 369 "V to U restore pkg eligible"; 370 case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_PKG_NOT_ELIGIBLE -> 371 "V to U restore pkg not eligible"; 372 case BackupManagerMonitor.LOG_EVENT_ID_V_TO_U_RESTORE_SET_LIST -> 373 "V to U restore lists"; 374 case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_AT_INSTALL_INVOKED -> 375 "Invoked restore at install"; 376 case BackupManagerMonitor.LOG_EVENT_ID_SKIP_RESTORE_AT_INSTALL -> 377 "Skip restore at install"; 378 case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_ACCEPTED_FOR_RESTORE -> 379 "Pkg accepted for restore"; 380 case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_DATA_DOES_NOT_BELONG_TO_PACKAGE -> 381 "Restore data does not belong to package"; 382 case BackupManagerMonitor.LOG_EVENT_ID_UNABLE_TO_CREATE_AGENT_FOR_RESTORE -> 383 "Unable to create Agent"; 384 case BackupManagerMonitor.LOG_EVENT_ID_AGENT_CRASHED_BEFORE_RESTORE_DATA_IS_SENT -> 385 "Agent crashed before restore data is streamed"; 386 case BackupManagerMonitor.LOG_EVENT_ID_FAILED_TO_SEND_DATA_TO_AGENT_DURING_RESTORE -> 387 "Failed to send data to agent"; 388 case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE_DURING_RESTORE -> 389 "Agent failure during restore"; 390 case BackupManagerMonitor.LOG_EVENT_ID_FAILED_TO_READ_DATA_FROM_TRANSPORT -> 391 "Failed to read data from Transport"; 392 default -> "Unknown log event ID: " + code; 393 }; 394 return id; 395 } 396 isOpTypeRestore(Bundle eventBundle)397 private boolean isOpTypeRestore(Bundle eventBundle) { 398 return switch (eventBundle.getInt( 399 BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, -1)) { 400 case BackupAnnotations.OperationType.RESTORE -> true; 401 default -> false; 402 }; 403 } 404 405 /** 406 * Store the timestamp when the device was set up (date when the first BMM event is parsed) 407 * in a text file. 408 */ 409 @VisibleForTesting recordSetUpTimestamp()410 void recordSetUpTimestamp() { 411 File setupDateFile = getSetUpDateFile(); 412 // record setup timestamp only once 413 if (setupDateFile.length() == 0) { 414 try (FileOutputStream out = new FileOutputStream(setupDateFile, /*append*/ true); 415 PrintWriter pw = new FastPrintWriter(out);) { 416 long currentDate = System.currentTimeMillis(); 417 pw.println(currentDate); 418 } catch (IOException e) { 419 Slog.w(TAG, "An error occurred while recording the setup date: " 420 + e.getMessage()); 421 } 422 } 423 424 } 425 426 @VisibleForTesting getSetUpDate()427 String getSetUpDate() { 428 File fname = getSetUpDateFile(); 429 try (FileInputStream inputStream = new FileInputStream(fname); 430 InputStreamReader reader = new InputStreamReader(inputStream); 431 BufferedReader bufferedReader = new BufferedReader(reader);) { 432 return bufferedReader.readLine(); 433 } catch (Exception e) { 434 Slog.w(TAG, "An error occurred while reading the date: " + e.getMessage()); 435 return "Could not retrieve setup date"; 436 } 437 } 438 439 @VisibleForTesting isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec)440 static boolean isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec) { 441 if (startTimeStamp > endTimeStamp) { 442 // Something has gone wrong, timeStamp1 should always precede timeStamp2. 443 // Out of caution return true: we would delete the logs rather than 444 // risking them being kept for longer than the retention period 445 return true; 446 } 447 long timeDifferenceMillis = endTimeStamp - startTimeStamp; 448 return (timeDifferenceMillis >= millisec); 449 } 450 451 /** 452 * Check if current date is after retention period 453 */ 454 @VisibleForTesting isAfterRetentionPeriod()455 boolean isAfterRetentionPeriod() { 456 if (mIsAfterRetentionPeriodCached) { 457 return mIsAfterRetentionPeriod; 458 } else { 459 File setUpDateFile = getSetUpDateFile(); 460 if (setUpDateFile.length() == 0) { 461 // We are yet to record a setup date. This means we haven't parsed the first event. 462 mIsAfterRetentionPeriod = false; 463 mIsAfterRetentionPeriodCached = true; 464 return false; 465 } 466 try { 467 long setupTimestamp = Long.parseLong(getSetUpDate()); 468 long currentTimestamp = System.currentTimeMillis(); 469 mIsAfterRetentionPeriod = isDateAfterNMillisec(setupTimestamp, currentTimestamp, 470 getRetentionPeriodInMillisec()); 471 mIsAfterRetentionPeriodCached = true; 472 return mIsAfterRetentionPeriod; 473 } catch (NumberFormatException e) { 474 // An error occurred when parsing the setup timestamp. 475 // Out of caution return true: we would delete the logs rather than 476 // risking them being kept for longer than the retention period 477 mIsAfterRetentionPeriod = true; 478 mIsAfterRetentionPeriodCached = true; 479 return true; 480 } 481 } 482 } 483 484 @VisibleForTesting getSetUpDateFile()485 File getSetUpDateFile() { 486 File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR); 487 File setupDateFile = new File(dataDir, INITIAL_SETUP_TIMESTAMP_KEY + ".txt"); 488 return setupDateFile; 489 } 490 491 @VisibleForTesting getRetentionPeriodInMillisec()492 long getRetentionPeriodInMillisec() { 493 return LOGS_RETENTION_PERIOD_MILLISEC; 494 } 495 496 @VisibleForTesting getBMMEventsFileSizeLimit()497 long getBMMEventsFileSizeLimit(){ 498 return BMM_FILE_SIZE_LIMIT_BYTES; 499 } 500 501 /** 502 * Delete the BMM Events file after the retention period has passed. 503 * 504 * @return true if the retention period has passed false otherwise. 505 * we want to return true even if we were unable to delete the file, as this will prevent 506 * expired BMM events from being printed to the dumpsys 507 */ deleteExpiredBMMEvents()508 public boolean deleteExpiredBMMEvents() { 509 try { 510 if (isAfterRetentionPeriod()) { 511 File bmmEvents = getBMMEventsFile(); 512 if (bmmEvents.exists()) { 513 if (bmmEvents.delete()) { 514 Slog.i(TAG, "Deleted expired BMM Events"); 515 } else { 516 Slog.e(TAG, "Unable to delete expired BMM Events"); 517 } 518 } 519 return true; 520 } 521 return false; 522 } catch (Exception e) { 523 // Handle any unexpected exceptions 524 // To be safe we return true as we want to avoid exposing expired BMMEvents 525 return true; 526 } 527 } 528 } 529