1 /* 2 * Copyright (C) 2024 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.appsearch.indexer; 18 19 import android.annotation.CurrentTimeMillisLong; 20 import android.annotation.NonNull; 21 import android.annotation.WorkerThread; 22 import android.os.PersistableBundle; 23 import android.util.AtomicFile; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.util.Objects; 32 33 /** 34 * Settings backed by a PersistableBundle, providing common functionality for settings handling. 35 * 36 * <p>This class provides common functionality for settings handling, including: 37 * 38 * <ul> 39 * <li>getting and setting the timestamp of the last update 40 * <li>loading and persisting settings to/from a file 41 * </ul> 42 * 43 * <p>This class is NOT thread safe (similar to {@link PersistableBundle} which it wraps). 44 */ 45 public abstract class IndexerSettings { 46 47 public static final String LAST_UPDATE_TIMESTAMP_KEY = "last_update_timestamp_millis"; 48 public static final String LAST_ATTEMPTED_UPDATE_TIMESTAMP_KEY = 49 "last_attempted_update_timestamp_millis"; 50 51 private final File mBaseDir; 52 private File mFile; 53 protected PersistableBundle mBundle = new PersistableBundle(); 54 IndexerSettings(@onNull File baseDir)55 public IndexerSettings(@NonNull File baseDir) { 56 mBaseDir = Objects.requireNonNull(baseDir); 57 } 58 59 /** Allows for late initialization of the settings file. */ 60 @WorkerThread ensureFileCreated()61 private void ensureFileCreated() { 62 if (mFile != null) { 63 return; 64 } 65 mFile = new File(mBaseDir, getSettingsFileName()); 66 } 67 getSettingsFileName()68 protected abstract String getSettingsFileName(); 69 70 /** Loads the bundle from the file. */ 71 @WorkerThread load()72 public void load() throws IOException { 73 ensureFileCreated(); 74 mBundle = readBundle(mFile); 75 } 76 77 /** Saves the bundle to the file. */ 78 @WorkerThread persist()79 public void persist() throws IOException { 80 ensureFileCreated(); 81 writeBundle(mFile, mBundle); 82 } 83 84 /** Returns the timestamp of when the last update occurred in milliseconds. */ getLastUpdateTimestampMillis()85 public @CurrentTimeMillisLong long getLastUpdateTimestampMillis() { 86 return mBundle.getLong(LAST_UPDATE_TIMESTAMP_KEY); 87 } 88 89 /** Sets the timestamp of when the last update occurred in milliseconds. */ setLastUpdateTimestampMillis(@urrentTimeMillisLong long timestampMillis)90 public void setLastUpdateTimestampMillis(@CurrentTimeMillisLong long timestampMillis) { 91 mBundle.putLong(LAST_UPDATE_TIMESTAMP_KEY, timestampMillis); 92 } 93 94 /** Resets all the settings to default values. */ reset()95 public void reset() { 96 setLastUpdateTimestampMillis(0); 97 setLastAttemptedUpdateTimestampMillis(0); 98 } 99 100 /** Static util method to read a bundle from a file. */ 101 @VisibleForTesting 102 @NonNull 103 @WorkerThread readBundle(@onNull File src)104 public static PersistableBundle readBundle(@NonNull File src) throws IOException { 105 AtomicFile atomicFile = new AtomicFile(src); 106 try (FileInputStream fis = atomicFile.openRead()) { 107 return PersistableBundle.readFromStream(fis); 108 } 109 } 110 111 /** Static util method to write a bundle to a file. */ 112 @VisibleForTesting 113 @WorkerThread writeBundle(@onNull File dest, @NonNull PersistableBundle bundle)114 public static void writeBundle(@NonNull File dest, @NonNull PersistableBundle bundle) 115 throws IOException { 116 AtomicFile atomicFile = new AtomicFile(dest); 117 FileOutputStream fos = null; 118 try { 119 fos = atomicFile.startWrite(); 120 bundle.writeToStream(fos); 121 atomicFile.finishWrite(fos); 122 } catch (IOException e) { 123 if (fos != null) { 124 atomicFile.failWrite(fos); 125 } 126 throw e; 127 } 128 } 129 130 /** Returns the timestamp of when the last update occurred in milliseconds. */ getLastAttemptedUpdateTimestampMillis()131 public @CurrentTimeMillisLong long getLastAttemptedUpdateTimestampMillis() { 132 return mBundle.getLong(LAST_ATTEMPTED_UPDATE_TIMESTAMP_KEY); 133 } 134 135 /** Sets the timestamp of when the last update occurred in milliseconds. */ setLastAttemptedUpdateTimestampMillis(@urrentTimeMillisLong long timestampMillis)136 public void setLastAttemptedUpdateTimestampMillis(@CurrentTimeMillisLong long timestampMillis) { 137 mBundle.putLong(LAST_ATTEMPTED_UPDATE_TIMESTAMP_KEY, timestampMillis); 138 } 139 } 140