All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.backup.encryption.keys.TertiaryKeyRotationWindowedCount Maven / Gradle / Ivy

/*
 * Copyright (C) 2019 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.server.backup.encryption.keys;

import android.content.Context;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Clock;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * Tracks (and commits to disk) how many key rotations have happened in the last 24 hours. This
 * allows us to limit (and therefore stagger) the number of key rotations in a given period of time.
 *
 * 

Note to engineers thinking of replacing the below with fancier algorithms and data structures: * we expect the total size of this count at any time to be below however many rotations we allow in * the window, which is going to be in single digits. Any changes that mean we write to disk more * frequently, that the code is no longer resistant to clock changes, or that the code is more * difficult to understand are almost certainly not worthwhile. */ public class TertiaryKeyRotationWindowedCount { private static final String TAG = "TertiaryKeyRotCount"; private static final int WINDOW_IN_HOURS = 24; private static final String LOG_FILE_NAME = "tertiary_key_rotation_windowed_count"; private final Clock mClock; private final File mFile; private ArrayList mEvents; /** Returns a new instance, persisting state to the files dir of {@code context}. */ public static TertiaryKeyRotationWindowedCount getInstance(Context context) { File logFile = new File(context.getFilesDir(), LOG_FILE_NAME); return new TertiaryKeyRotationWindowedCount(logFile, Clock.systemDefaultZone()); } /** A new instance, committing state to {@code file}, and reading time from {@code clock}. */ @VisibleForTesting TertiaryKeyRotationWindowedCount(File file, Clock clock) { mFile = file; mClock = clock; mEvents = new ArrayList<>(); try { loadFromFile(); } catch (IOException e) { Slog.e(TAG, "Error reading " + LOG_FILE_NAME, e); } } /** Records a key rotation at the current time. */ public void record() { mEvents.add(mClock.millis()); compact(); try { saveToFile(); } catch (IOException e) { Slog.e(TAG, "Error saving " + LOG_FILE_NAME, e); } } /** Returns the number of key rotation that have been recorded in the window. */ public int getCount() { compact(); return mEvents.size(); } private void compact() { long minimumTimestamp = getMinimumTimestamp(); long now = mClock.millis(); ArrayList compacted = new ArrayList<>(); for (long event : mEvents) { if (event >= minimumTimestamp && event <= now) { compacted.add(event); } } mEvents = compacted; } private long getMinimumTimestamp() { return mClock.millis() - TimeUnit.HOURS.toMillis(WINDOW_IN_HOURS) + 1; } private void loadFromFile() throws IOException { if (!mFile.exists()) { return; } try (FileInputStream fis = new FileInputStream(mFile); DataInputStream dis = new DataInputStream(fis)) { while (true) { mEvents.add(dis.readLong()); } } catch (EOFException eof) { // expected } } private void saveToFile() throws IOException { // File size is maximum number of key rotations in window multiplied by 8 bytes, which is // why // we just overwrite it each time. We expect it will always be less than 100 bytes in size. try (FileOutputStream fos = new FileOutputStream(mFile); DataOutputStream dos = new DataOutputStream(fos)) { for (long event : mEvents) { dos.writeLong(event); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy