blob: f16a68d642131f96ea7f0b45dabdce1d057789ee [file] [log] [blame]
/*
* 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 com.android.internal.annotations.VisibleForTesting;
/**
* Schedules tertiary key rotations in a staggered fashion.
*
* <p>Apps are due a key rotation after a certain number of backups. Rotations are then staggerered
* over a period of time, through restricting the number of rotations allowed in a 24-hour window.
* This will causes the apps to enter a staggered cycle of regular rotations.
*
* <p>Note: the methods in this class are not optimized to be super fast. They make blocking IO to
* ensure that scheduler information is committed to disk, so that it is available after the user
* turns their device off and on. This ought to be fine as
*
* <ul>
* <li>It will be invoked before a backup, so should never be invoked on the UI thread
* <li>It will be invoked before a backup, so the vast amount of time is spent on the backup, not
* writing tiny amounts of data to disk.
* </ul>
*/
public class TertiaryKeyRotationScheduler {
/** Default number of key rotations allowed within 24 hours. */
private static final int KEY_ROTATION_LIMIT = 2;
/** A new instance, using {@code context} to determine where to store state. */
public static TertiaryKeyRotationScheduler getInstance(Context context) {
TertiaryKeyRotationWindowedCount windowedCount =
TertiaryKeyRotationWindowedCount.getInstance(context);
TertiaryKeyRotationTracker tracker = TertiaryKeyRotationTracker.getInstance(context);
return new TertiaryKeyRotationScheduler(tracker, windowedCount, KEY_ROTATION_LIMIT);
}
private final TertiaryKeyRotationTracker mTracker;
private final TertiaryKeyRotationWindowedCount mWindowedCount;
private final int mMaximumRotationsPerWindow;
/**
* A new instance.
*
* @param tracker Tracks how many times each application has backed up.
* @param windowedCount Tracks how many rotations have happened in the last 24 hours.
* @param maximumRotationsPerWindow The maximum number of key rotations allowed per 24 hours.
*/
@VisibleForTesting
TertiaryKeyRotationScheduler(
TertiaryKeyRotationTracker tracker,
TertiaryKeyRotationWindowedCount windowedCount,
int maximumRotationsPerWindow) {
mTracker = tracker;
mWindowedCount = windowedCount;
mMaximumRotationsPerWindow = maximumRotationsPerWindow;
}
/**
* Returns {@code true} if the app with {@code packageName} is due having its key rotated.
*
* <p>This ought to be queried before backing up an app, to determine whether to do an
* incremental backup or a full backup. (A full backup forces key rotation.)
*/
public boolean isKeyRotationDue(String packageName) {
if (mWindowedCount.getCount() >= mMaximumRotationsPerWindow) {
return false;
}
return mTracker.isKeyRotationDue(packageName);
}
/**
* Records that a backup happened for the app with the given {@code packageName}.
*
* <p>Each backup brings the app closer to the point at which a key rotation is due.
*/
public void recordBackup(String packageName) {
mTracker.recordBackup(packageName);
}
/**
* Records a key rotation happened for the app with the given {@code packageName}.
*
* <p>This resets the countdown until the next key rotation is due.
*/
public void recordKeyRotation(String packageName) {
mTracker.resetCountdown(packageName);
mWindowedCount.record();
}
}