blob: f2c158658562d08bc701e0fcc7f4ab123bf298e0 [file] [log] [blame]
Sudheer Shankaab1d4162020-01-07 10:37:50 -08001/*
2 * Copyright 2020 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 */
16package com.android.server.blob;
17
Sudheer Shanka364364b2020-02-19 17:56:09 -080018import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE;
19import static android.text.format.Formatter.FLAG_IEC_UNITS;
20import static android.text.format.Formatter.formatFileSize;
21import static android.util.TimeUtils.formatDuration;
22
Sudheer Shankaab1d4162020-01-07 10:37:50 -080023import android.annotation.NonNull;
24import android.annotation.Nullable;
Sudheer Shanka364364b2020-02-19 17:56:09 -080025import android.content.Context;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080026import android.os.Environment;
Sudheer Shanka364364b2020-02-19 17:56:09 -080027import android.provider.DeviceConfig;
28import android.provider.DeviceConfig.Properties;
29import android.util.DataUnit;
Sudheer Shankae53e1ed2020-02-03 17:15:24 -080030import android.util.Log;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080031import android.util.Slog;
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -070032import android.util.TimeUtils;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080033
Sudheer Shanka364364b2020-02-19 17:56:09 -080034import com.android.internal.util.IndentingPrintWriter;
35
Sudheer Shankaab1d4162020-01-07 10:37:50 -080036import java.io.File;
Sudheer Shankaae53d112020-01-31 14:20:53 -080037import java.util.concurrent.TimeUnit;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080038
39class BlobStoreConfig {
40 public static final String TAG = "BlobStore";
Sudheer Shankae53e1ed2020-02-03 17:15:24 -080041 public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
Sudheer Shankaab1d4162020-01-07 10:37:50 -080042
Sudheer Shanka1406bc82020-02-03 12:27:24 -080043 // Initial version.
44 public static final int XML_VERSION_INIT = 1;
45 // Added a string variant of lease description.
46 public static final int XML_VERSION_ADD_STRING_DESC = 2;
Sudheer Shanka1b6b8252020-03-04 22:19:21 -080047 public static final int XML_VERSION_ADD_DESC_RES_NAME = 3;
Sudheer Shanka1406bc82020-02-03 12:27:24 -080048
Sudheer Shanka1b6b8252020-03-04 22:19:21 -080049 public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_DESC_RES_NAME;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080050
Sudheer Shanka9f2296a2020-01-25 23:22:34 -080051 private static final String ROOT_DIR_NAME = "blobstore";
52 private static final String BLOBS_DIR_NAME = "blobs";
53 private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml";
54 private static final String BLOBS_INDEX_FILE_NAME = "blobs_index.xml";
55
Sudheer Shankaae53d112020-01-31 14:20:53 -080056 /**
57 * Job Id for idle maintenance job ({@link BlobStoreIdleJobService}).
58 */
59 public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L
60 /**
61 * Max time period (in millis) between each idle maintenance job run.
62 */
63 public static final long IDLE_JOB_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
64
65 /**
66 * Timeout in millis after which sessions with no updates will be deleted.
67 */
68 public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7);
69
Sudheer Shanka364364b2020-02-19 17:56:09 -080070 public static class DeviceConfigProperties {
71 /**
72 * Denotes how low the limit for the amount of data, that an app will be allowed to acquire
73 * a lease on, can be.
74 */
75 public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
76 "total_bytes_per_app_limit_floor";
77 public static final long DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
78 DataUnit.MEBIBYTES.toBytes(300); // 300 MiB
79 public static long TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
80 DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR;
81
82 /**
83 * Denotes the maximum amount of data an app can acquire a lease on, in terms of fraction
84 * of total disk space.
85 */
86 public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
87 "total_bytes_per_app_limit_fraction";
88 public static final float DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION = 0.01f;
89 public static float TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
90 DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION;
91
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -070092 /**
93 * Denotes the duration from the time a blob is committed that we wait for a lease to
94 * be acquired before deciding to delete the blob for having no leases.
95 */
96 public static final String KEY_LEASE_ACQUISITION_WAIT_DURATION_MS =
97 "lease_acquisition_wait_time_ms";
98 public static final long DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS =
99 TimeUnit.HOURS.toMillis(6);
100 public static long LEASE_ACQUISITION_WAIT_DURATION_MS =
101 DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS;
102
Sudheer Shanka364364b2020-02-19 17:56:09 -0800103 static void refresh(Properties properties) {
104 if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
105 return;
106 }
107 properties.getKeyset().forEach(key -> {
108 switch (key) {
109 case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR:
110 TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key,
111 DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR);
112 break;
113 case KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION:
114 TOTAL_BYTES_PER_APP_LIMIT_FRACTION = properties.getFloat(key,
115 DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
116 break;
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700117 case KEY_LEASE_ACQUISITION_WAIT_DURATION_MS:
118 LEASE_ACQUISITION_WAIT_DURATION_MS = properties.getLong(key,
119 DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS);
120 break;
Sudheer Shanka364364b2020-02-19 17:56:09 -0800121 default:
122 Slog.wtf(TAG, "Unknown key in device config properties: " + key);
123 }
124 });
125 }
126
127 static void dump(IndentingPrintWriter fout, Context context) {
128 final String dumpFormat = "%s: [cur: %s, def: %s]";
129 fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
130 formatFileSize(context, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS),
131 formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
132 FLAG_IEC_UNITS)));
133 fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
134 TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
135 DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION));
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700136 fout.println(String.format(dumpFormat, KEY_LEASE_ACQUISITION_WAIT_DURATION_MS,
137 TimeUtils.formatDuration(LEASE_ACQUISITION_WAIT_DURATION_MS),
138 TimeUtils.formatDuration(DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS)));
Sudheer Shanka364364b2020-02-19 17:56:09 -0800139 }
140 }
141
142 public static void initialize(Context context) {
143 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_BLOBSTORE,
144 context.getMainExecutor(),
145 properties -> DeviceConfigProperties.refresh(properties));
146 DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE));
147 }
148
149 /**
150 * Returns the maximum amount of data that an app can acquire a lease on.
151 */
152 public static long getAppDataBytesLimit() {
153 final long totalBytesLimit = (long) (Environment.getDataSystemDirectory().getTotalSpace()
154 * DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
155 return Math.max(DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FLOOR, totalBytesLimit);
156 }
157
Sudheer Shankac0fd5fa2020-03-15 23:31:37 -0700158 /**
159 * Returns whether the wait time for lease acquisition for a blob has elapsed.
160 */
161 public static boolean hasLeaseWaitTimeElapsed(long commitTimeMs) {
162 return commitTimeMs + DeviceConfigProperties.LEASE_ACQUISITION_WAIT_DURATION_MS
163 < System.currentTimeMillis();
164 }
165
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800166 @Nullable
167 public static File prepareBlobFile(long sessionId) {
168 final File blobsDir = prepareBlobsDir();
169 return blobsDir == null ? null : getBlobFile(blobsDir, sessionId);
170 }
171
172 @NonNull
173 public static File getBlobFile(long sessionId) {
174 return getBlobFile(getBlobsDir(), sessionId);
175 }
176
177 @NonNull
178 private static File getBlobFile(File blobsDir, long sessionId) {
179 return new File(blobsDir, String.valueOf(sessionId));
180 }
181
182 @Nullable
183 public static File prepareBlobsDir() {
184 final File blobsDir = getBlobsDir(prepareBlobStoreRootDir());
185 if (!blobsDir.exists() && !blobsDir.mkdir()) {
186 Slog.e(TAG, "Failed to mkdir(): " + blobsDir);
187 return null;
188 }
189 return blobsDir;
190 }
191
192 @NonNull
193 public static File getBlobsDir() {
194 return getBlobsDir(getBlobStoreRootDir());
195 }
196
197 @NonNull
198 private static File getBlobsDir(File blobsRootDir) {
Sudheer Shanka9f2296a2020-01-25 23:22:34 -0800199 return new File(blobsRootDir, BLOBS_DIR_NAME);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800200 }
201
202 @Nullable
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800203 public static File prepareSessionIndexFile() {
204 final File blobStoreRootDir = prepareBlobStoreRootDir();
205 if (blobStoreRootDir == null) {
206 return null;
207 }
Sudheer Shanka9f2296a2020-01-25 23:22:34 -0800208 return new File(blobStoreRootDir, SESSIONS_INDEX_FILE_NAME);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800209 }
210
211 @Nullable
212 public static File prepareBlobsIndexFile() {
213 final File blobsStoreRootDir = prepareBlobStoreRootDir();
214 if (blobsStoreRootDir == null) {
215 return null;
216 }
Sudheer Shanka9f2296a2020-01-25 23:22:34 -0800217 return new File(blobsStoreRootDir, BLOBS_INDEX_FILE_NAME);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800218 }
219
220 @Nullable
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800221 public static File prepareBlobStoreRootDir() {
222 final File blobStoreRootDir = getBlobStoreRootDir();
223 if (!blobStoreRootDir.exists() && !blobStoreRootDir.mkdir()) {
224 Slog.e(TAG, "Failed to mkdir(): " + blobStoreRootDir);
225 return null;
226 }
227 return blobStoreRootDir;
228 }
229
230 @NonNull
231 public static File getBlobStoreRootDir() {
Sudheer Shanka9f2296a2020-01-25 23:22:34 -0800232 return new File(Environment.getDataSystemDirectory(), ROOT_DIR_NAME);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800233 }
Sudheer Shanka364364b2020-02-19 17:56:09 -0800234
235 public static void dump(IndentingPrintWriter fout, Context context) {
236 fout.println("XML current version: " + XML_VERSION_CURRENT);
237
238 fout.println("Idle job ID: " + IDLE_JOB_ID);
239 fout.println("Idle job period: " + formatDuration(IDLE_JOB_PERIOD_MILLIS));
240
241 fout.println("Session expiry timeout: " + formatDuration(SESSION_EXPIRY_TIMEOUT_MILLIS));
242
243 fout.println("Total bytes per app limit: " + formatFileSize(context,
244 getAppDataBytesLimit(), FLAG_IEC_UNITS));
245
246 fout.println("Device config properties:");
247 fout.increaseIndent();
248 DeviceConfigProperties.dump(fout, context);
249 fout.decreaseIndent();
250 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800251}