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.appsindexer;
18 
19 import android.annotation.NonNull;
20 import android.os.PersistableBundle;
21 import android.util.AtomicFile;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.util.Objects;
30 
31 /**
32  * Apps indexer settings backed by a PersistableBundle.
33  *
34  * <p>Holds settings such as:
35  *
36  * <ul>
37  *   <li>the last time a full update was performed
38  *   <li>the time of the last apps update
39  *   <li>the time of the last apps deletion
40  * </ul>
41  *
42  * <p>This class is NOT thread safe (similar to {@link PersistableBundle} which it wraps).
43  *
44  * @hide
45  */
46 public class AppsIndexerSettings {
47     static final String SETTINGS_FILE_NAME = "apps_indexer_settings.pb";
48     static final String LAST_UPDATE_TIMESTAMP_KEY = "last_update_timestamp_millis";
49     static final String LAST_APP_UPDATE_TIMESTAMP_KEY = "last_app_update_timestamp_millis";
50 
51     private final File mFile;
52     private PersistableBundle mBundle = new PersistableBundle();
53 
AppsIndexerSettings(@onNull File baseDir)54     public AppsIndexerSettings(@NonNull File baseDir) {
55         Objects.requireNonNull(baseDir);
56         mFile = new File(baseDir, SETTINGS_FILE_NAME);
57     }
58 
load()59     public void load() throws IOException {
60         mBundle = readBundle(mFile);
61     }
62 
persist()63     public void persist() throws IOException {
64         writeBundle(mFile, mBundle);
65     }
66 
67     /** Returns the timestamp of when the last full update occurred in milliseconds. */
getLastUpdateTimestampMillis()68     public long getLastUpdateTimestampMillis() {
69         return mBundle.getLong(LAST_UPDATE_TIMESTAMP_KEY);
70     }
71 
72     /** Sets the timestamp of when the last full update occurred in milliseconds. */
setLastUpdateTimestampMillis(long timestampMillis)73     public void setLastUpdateTimestampMillis(long timestampMillis) {
74         mBundle.putLong(LAST_UPDATE_TIMESTAMP_KEY, timestampMillis);
75     }
76 
77     /** Returns the timestamp of when the last app was updated in milliseconds. */
getLastAppUpdateTimestampMillis()78     public long getLastAppUpdateTimestampMillis() {
79         return mBundle.getLong(LAST_APP_UPDATE_TIMESTAMP_KEY);
80     }
81 
82     /** Sets the timestamp of when the last apps was updated in milliseconds. */
setLastAppUpdateTimestampMillis(long timestampMillis)83     public void setLastAppUpdateTimestampMillis(long timestampMillis) {
84         mBundle.putLong(LAST_APP_UPDATE_TIMESTAMP_KEY, timestampMillis);
85     }
86 
87     /** Resets all the settings to default values. */
reset()88     public void reset() {
89         setLastUpdateTimestampMillis(0);
90         setLastAppUpdateTimestampMillis(0);
91     }
92 
93     @VisibleForTesting
94     @NonNull
readBundle(@onNull File src)95     static PersistableBundle readBundle(@NonNull File src) throws IOException {
96         AtomicFile atomicFile = new AtomicFile(src);
97         try (FileInputStream fis = atomicFile.openRead()) {
98             return PersistableBundle.readFromStream(fis);
99         }
100     }
101 
102     @VisibleForTesting
writeBundle(@onNull File dest, @NonNull PersistableBundle bundle)103     static void writeBundle(@NonNull File dest, @NonNull PersistableBundle bundle)
104             throws IOException {
105         AtomicFile atomicFile = new AtomicFile(dest);
106         FileOutputStream fos = null;
107         try {
108             fos = atomicFile.startWrite();
109             bundle.writeToStream(fos);
110             atomicFile.finishWrite(fos);
111         } catch (IOException e) {
112             if (fos != null) {
113                 atomicFile.failWrite(fos);
114             }
115             throw e;
116         }
117     }
118 }
119