/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.dialer.persistentlog; import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.os.Build.VERSION_CODES; import android.preference.PreferenceManager; import android.support.annotation.AnyThread; import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.support.v4.os.UserManagerCompat; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Handles serialization of byte arrays and read/write them to multiple rotating files. If a logText * file exceeds {@code fileSizeLimit} after a write, a new file will be used. if the total number of * files exceeds {@code fileCountLimit} the oldest ones will be deleted. The logs are stored in the * cache but the file index is stored in the data (clearing data will also clear the cache). The * logs will be stored under /cache_dir/persistent_log/{@code subfolder}, so multiple independent * logs can be created. * *
This class is NOT thread safe. All methods expect the constructor must be called on the same
* worker thread.
*/
@SuppressWarnings("AndroidApiChecker") // lambdas
@TargetApi(VERSION_CODES.M)
final class PersistentLogFileHandler {
private static final String LOG_DIRECTORY = "persistent_log";
private static final String NEXT_FILE_INDEX_PREFIX = "persistent_long_next_file_index_";
private File logDirectory;
private final String subfolder;
private final int fileSizeLimit;
private final int fileCountLimit;
private SharedPreferences sharedPreferences;
private File outputFile;
private Context context;
@MainThread
PersistentLogFileHandler(String subfolder, int fileSizeLimit, int fileCountLimit) {
this.subfolder = subfolder;
this.fileSizeLimit = fileSizeLimit;
this.fileCountLimit = fileCountLimit;
}
/** Must be called right after the logger thread is created. */
@WorkerThread
void initialize(Context context) {
this.context = context;
logDirectory = new File(new File(context.getCacheDir(), LOG_DIRECTORY), subfolder);
initializeSharedPreference(context);
}
@WorkerThread
private boolean initializeSharedPreference(Context context) {
if (sharedPreferences == null && UserManagerCompat.isUserUnlocked(context)) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
return true;
}
return sharedPreferences != null;
}
/**
* Write the list of byte arrays to the current log file, prefixing each entry with its' length. A
* new file will only be selected when the batch is completed, so the resulting file might be
* larger then {@code fileSizeLimit}
*/
@WorkerThread
void writeLogs(List