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