• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.contactsindexer;
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  * Contacts 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 last time a delta update was performed
39  *   <li>the time of the last CP2 contact update
40  *   <li>the time of the last CP2 contact deletion
41  * </ul>
42  *
43  * <p>This class is NOT thread safe (similar to {@link PersistableBundle} which it wraps).
44  *
45  * @hide
46  */
47 public class ContactsIndexerSettings {
48 
49     private static final String TAG = "ContactsIndexerSettings";
50 
51     static final String SETTINGS_FILE_NAME = "contacts_indexer_settings.pb";
52     static final String LAST_FULL_UPDATE_TIMESTAMP_KEY = "last_full_update_timestamp_millis";
53     static final String LAST_DELTA_UPDATE_TIMESTAMP_KEY = "last_delta_update_timestamp_key";
54     // TODO(b/296078517): rename the keys to match the constants but keep backwards compatibility
55     // Note this constant was renamed from LAST_DELTA_UPDATE_TIMESTAMP_KEY but the key itself has
56     // been kept the same for backwards compatibility
57     static final String LAST_CONTACT_UPDATE_TIMESTAMP_KEY = "last_delta_update_timestamp_millis";
58     // Note this constant was renamed from LAST_DELTA_DELETE_TIMESTAMP_KEY but the key itself has
59     // been kept the same for backwards compatibility
60     static final String LAST_CONTACT_DELETE_TIMESTAMP_KEY = "last_delta_delete_timestamp_millis";
61 
62     private final File mFile;
63     private PersistableBundle mBundle = new PersistableBundle();
64 
ContactsIndexerSettings(@onNull File baseDir)65     public ContactsIndexerSettings(@NonNull File baseDir) {
66         Objects.requireNonNull(baseDir);
67         mFile = new File(baseDir, SETTINGS_FILE_NAME);
68     }
69 
load()70     public void load() throws IOException {
71         mBundle = readBundle(mFile);
72     }
73 
persist()74     public void persist() throws IOException {
75         writeBundle(mFile, mBundle);
76     }
77 
78     /** Returns the timestamp of when the last full update occurred in milliseconds. */
getLastFullUpdateTimestampMillis()79     public long getLastFullUpdateTimestampMillis() {
80         return mBundle.getLong(LAST_FULL_UPDATE_TIMESTAMP_KEY);
81     }
82 
83     /** Sets the timestamp of when the last full update occurred in milliseconds. */
setLastFullUpdateTimestampMillis(long timestampMillis)84     public void setLastFullUpdateTimestampMillis(long timestampMillis) {
85         mBundle.putLong(LAST_FULL_UPDATE_TIMESTAMP_KEY, timestampMillis);
86     }
87 
88     /** Returns the timestamp of when the last delta update occurred in milliseconds. */
getLastDeltaUpdateTimestampMillis()89     public long getLastDeltaUpdateTimestampMillis() {
90         return mBundle.getLong(LAST_DELTA_UPDATE_TIMESTAMP_KEY);
91     }
92 
93     /** Sets the timestamp of when the last delta update occurred in milliseconds. */
setLastDeltaUpdateTimestampMillis(long timestampMillis)94     public void setLastDeltaUpdateTimestampMillis(long timestampMillis) {
95         mBundle.putLong(LAST_DELTA_UPDATE_TIMESTAMP_KEY, timestampMillis);
96     }
97 
98     /** Returns the timestamp of when the last contact in CP2 was updated in milliseconds. */
getLastContactUpdateTimestampMillis()99     public long getLastContactUpdateTimestampMillis() {
100         return mBundle.getLong(LAST_CONTACT_UPDATE_TIMESTAMP_KEY);
101     }
102 
103     /** Sets the timestamp of when the last contact in CP2 was updated in milliseconds. */
setLastContactUpdateTimestampMillis(long timestampMillis)104     public void setLastContactUpdateTimestampMillis(long timestampMillis) {
105         mBundle.putLong(LAST_CONTACT_UPDATE_TIMESTAMP_KEY, timestampMillis);
106     }
107 
108     /** Returns the timestamp of when the last contact in CP2 was deleted in milliseconds. */
getLastContactDeleteTimestampMillis()109     public long getLastContactDeleteTimestampMillis() {
110         return mBundle.getLong(LAST_CONTACT_DELETE_TIMESTAMP_KEY);
111     }
112 
113     /** Sets the timestamp of when the last contact in CP2 was deleted in milliseconds. */
setLastContactDeleteTimestampMillis(long timestampMillis)114     public void setLastContactDeleteTimestampMillis(long timestampMillis) {
115         mBundle.putLong(LAST_CONTACT_DELETE_TIMESTAMP_KEY, timestampMillis);
116     }
117 
118     /** Resets all the settings to default values. */
reset()119     public void reset() {
120         setLastFullUpdateTimestampMillis(0);
121         setLastDeltaUpdateTimestampMillis(0);
122         setLastContactUpdateTimestampMillis(0);
123         setLastContactDeleteTimestampMillis(0);
124     }
125 
126     @VisibleForTesting
127     @NonNull
readBundle(@onNull File src)128     static PersistableBundle readBundle(@NonNull File src) throws IOException {
129         AtomicFile atomicFile = new AtomicFile(src);
130         try (FileInputStream fis = atomicFile.openRead()) {
131             return PersistableBundle.readFromStream(fis);
132         }
133     }
134 
135     @VisibleForTesting
writeBundle(@onNull File dest, @NonNull PersistableBundle bundle)136     static void writeBundle(@NonNull File dest, @NonNull PersistableBundle bundle)
137             throws IOException {
138         AtomicFile atomicFile = new AtomicFile(dest);
139         FileOutputStream fos = null;
140         try {
141             fos = atomicFile.startWrite();
142             bundle.writeToStream(fos);
143             atomicFile.finishWrite(fos);
144         } catch (IOException e) {
145             if (fos != null) {
146                 atomicFile.failWrite(fos);
147             }
148             throw e;
149         }
150     }
151 }
152