diff options
author | Roshan Pius <rpius@google.com> | 2018-10-19 08:20:30 -0700 |
---|---|---|
committer | Roshan Pius <rpius@google.com> | 2018-11-01 07:00:38 -0700 |
commit | 0b86079855b2bc120a6c7790c53c927d95485b62 (patch) | |
tree | 40eaea3a2451c01ebe0cc1e7a3b2346c84d27c9e /service/java/com/android/server/wifi/WifiConfigStore.java | |
parent | e77a84b320e776d0e783d87b492d5f80ac6f9648 (diff) | |
download | android_frameworks_opt_net_wifi-0b86079855b2bc120a6c7790c53c927d95485b62.tar.gz android_frameworks_opt_net_wifi-0b86079855b2bc120a6c7790c53c927d95485b62.tar.bz2 android_frameworks_opt_net_wifi-0b86079855b2bc120a6c7790c53c927d95485b62.zip |
WifiConfigStore Refactor (1/2)
New WifiConfigStore Architecture:
a) WifiConfigStore can now manage multiple user store files (StoreFile).
It now manages a total of 3 files: 1 shared & 1 user (both existing) & 1 new one
specifically for network suggestions.
b) Clients(StoreData) should now register themselves to write/read from
any one of the store files.
c) |StoreFile|'s are only written if any one of the |StoreData|'s
registered for that file indicates that it has new data to write.
PS: Please refer to the bug for a summary of problems with the previous
architecture.
Bug: 117599800
Test: Unit tests
Test: Verified upgrading device with some store data to the new
architecture and ensured that we don't lose any data.
Test: Will send for integration tests.
Change-Id: Iebe54b6bef4d203540e7da490eae9851131d8827
Diffstat (limited to 'service/java/com/android/server/wifi/WifiConfigStore.java')
-rw-r--r-- | service/java/com/android/server/wifi/WifiConfigStore.java | 399 |
1 files changed, 275 insertions, 124 deletions
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java index 17a6670ff..00f09f571 100644 --- a/service/java/com/android/server/wifi/WifiConfigStore.java +++ b/service/java/com/android/server/wifi/WifiConfigStore.java @@ -16,6 +16,8 @@ package com.android.server.wifi; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.Context; @@ -24,11 +26,13 @@ import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.util.SparseArray; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.Preconditions; import com.android.server.wifi.util.XmlUtil; import org.xmlpull.v1.XmlPullParser; @@ -38,22 +42,56 @@ import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** - * This class provides the API's to save/load/modify network configurations from a persistent - * store. Uses keystore for certificate/key management operations. - * NOTE: This class should only be used from WifiConfigManager and is not thread-safe! + * This class provides a mechanism to save data to persistent store files {@link StoreFile}. + * Modules can register a {@link StoreData} instance indicating the {@StoreFile} into which they + * want to save their data to. + * + * NOTE: + * <li>Modules can register their {@StoreData} using + * {@link WifiConfigStore#registerStoreData(StoreData)} directly, but should + * use {@link WifiConfigManager#saveToStore(boolean)} for any writes.</li> + * <li>{@link WifiConfigManager} controls {@link WifiConfigStore} and initiates read at bootup and + * store file changes on user switch.</li> + * <li>Not thread safe!</li> */ public class WifiConfigStore { + /** + * Config store file for general shared store file. + */ + public static final int STORE_FILE_SHARED_GENERAL = 0; + /** + * Config store file for general user store file. + */ + public static final int STORE_FILE_USER_GENERAL = 1; + /** + * Config store file for network suggestions user store file. + */ + public static final int STORE_FILE_USER_NETWORK_SUGGESTIONS = 2; + + @IntDef(prefix = { "STORE_FILE_" }, value = { + STORE_FILE_SHARED_GENERAL, + STORE_FILE_USER_GENERAL, + STORE_FILE_USER_NETWORK_SUGGESTIONS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StoreFileId { } + private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData"; private static final String XML_TAG_VERSION = "Version"; /** @@ -76,10 +114,6 @@ public class WifiConfigStore { */ private static final String TAG = "WifiConfigStore"; /** - * Config store file name for both shared & user specific stores. - */ - private static final String STORE_FILE_NAME = "WifiConfigStore.xml"; - /** * Directory to store the config store files in. */ private static final String STORE_DIRECTORY_NAME = "wifi"; @@ -88,6 +122,29 @@ public class WifiConfigStore { */ private static final int BUFFERED_WRITE_ALARM_INTERVAL_MS = 10 * 1000; /** + * Config store file name for general shared store file. + */ + private static final String STORE_FILE_NAME_SHARED_GENERAL = "WifiConfigStore.xml"; + /** + * Config store file name for general user store file. + */ + private static final String STORE_FILE_NAME_USER_GENERAL = "WifiConfigStore.xml"; + /** + * Config store file name for network suggestions user store file. + */ + private static final String STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS = + "WifiConfigStoreNetworkSuggestions.xml"; + /** + * Mapping of Store file Id to Store file names. + */ + private static final SparseArray<String> STORE_ID_TO_FILE_NAME = + new SparseArray<String>() {{ + put(STORE_FILE_SHARED_GENERAL, STORE_FILE_NAME_SHARED_GENERAL); + put(STORE_FILE_USER_GENERAL, STORE_FILE_NAME_USER_GENERAL); + put(STORE_FILE_USER_NETWORK_SUGGESTIONS, STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS); + }}; + + /** * Handler instance to post alarm timeouts to */ private final Handler mEventHandler; @@ -100,13 +157,15 @@ public class WifiConfigStore { */ private final Clock mClock; /** - * Shared config store file instance. + * Shared config store file instance. There is 1 shared store file: + * {@link #STORE_FILE_NAME_SHARED_GENERAL}. */ private StoreFile mSharedStore; /** - * User specific store file instance. + * User specific store file instances. There are 2 user store files: + * {@link #STORE_FILE_NAME_USER_GENERAL} & {@link #STORE_FILE_NAME_USER_NETWORK_SUGGESTIONS}. */ - private StoreFile mUserStore; + private List<StoreFile> mUserStores; /** * Verbose logging flag. */ @@ -126,14 +185,13 @@ public class WifiConfigStore { } catch (IOException e) { Log.wtf(TAG, "Buffered write failed", e); } - } }; /** - * List of data container. + * List of data containers. */ - private final Map<String, StoreData> mStoreDataList; + private final List<StoreData> mStoreDataList; /** * Create a new instance of WifiConfigStore. @@ -151,33 +209,44 @@ public class WifiConfigStore { mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mEventHandler = new Handler(looper); mClock = clock; - mStoreDataList = new HashMap<>(); + mStoreDataList = new ArrayList<>(); // Initialize the store files. mSharedStore = sharedStore; // The user store is initialized to null, this will be set when the user unlocks and - // CE storage is accessible via |switchUserStoreAndRead|. - mUserStore = null; + // CE storage is accessible via |switchUserStoresAndRead|. + mUserStores = null; } - public void setUserStore(StoreFile userStore) { - mUserStore = userStore; + /** + * Set the user store files. + * (Useful for mocking in unit tests). + * @param userStores List of {@link StoreFile} created using {@link #createUserFiles(int)}. + */ + public void setUserStores(@NonNull List<StoreFile> userStores) { + Preconditions.checkNotNull(userStores); + mUserStores = userStores; } /** - * Register a {@link StoreData} to store. A {@link StoreData} is responsible - * for a block of data in the store file, and provides serialization/deserialization functions - * for those data. + * Register a {@link StoreData} to read/write data from/to a store. A {@link StoreData} is + * responsible for a block of data in the store file, and provides serialization/deserialization + * functions for those data. * * @param storeData The store data to be registered to the config store - * @return true if succeeded + * @return true if registered successfully, false if the store file name is not valid. */ - public boolean registerStoreData(StoreData storeData) { + public boolean registerStoreData(@NonNull StoreData storeData) { if (storeData == null) { Log.e(TAG, "Unable to register null store data"); return false; } - mStoreDataList.put(storeData.getName(), storeData); + int storeFileId = storeData.getStoreFileId(); + if (STORE_ID_TO_FILE_NAME.get(storeFileId) == null) { + Log.e(TAG, "Invalid shared store file specified" + storeFileId); + return false; + } + mStoreDataList.add(storeData); return true; } @@ -188,16 +257,18 @@ public class WifiConfigStore { * * @param storeBaseDir Base directory under which the store file is to be stored. The store file * will be at <storeBaseDir>/wifi/WifiConfigStore.xml. - * @return new instance of the store file. + * @param fileId Identifier for the file. See {@link StoreFileId}. + * @return new instance of the store file or null if the directory cannot be created. */ - private static StoreFile createFile(File storeBaseDir) { + private static StoreFile createFile(File storeBaseDir, @StoreFileId int fileId) { File storeDir = new File(storeBaseDir, STORE_DIRECTORY_NAME); if (!storeDir.exists()) { if (!storeDir.mkdir()) { Log.w(TAG, "Could not create store directory " + storeDir); + return null; } } - return new StoreFile(new File(storeDir, STORE_FILE_NAME)); + return new StoreFile(new File(storeDir, STORE_ID_TO_FILE_NAME.get(fileId)), fileId); } /** @@ -206,18 +277,23 @@ public class WifiConfigStore { * @return new instance of the store file or null if the directory cannot be created. */ public static StoreFile createSharedFile() { - return createFile(Environment.getDataMiscDirectory()); + return createFile(Environment.getDataMiscDirectory(), STORE_FILE_SHARED_GENERAL); } /** - * Create a new instance of the user specific store file. + * Create new instances of the user specific store files. * The user store file is inside the user's encrypted data directory. * * @param userId userId corresponding to the currently logged-in user. - * @return new instance of the store file or null if the directory cannot be created. - */ - public static StoreFile createUserFile(int userId) { - return createFile(Environment.getDataMiscCeDirectory(userId)); + * @return List of new instances of the store files created. + */ + public static List<StoreFile> createUserFiles(int userId) { + List<StoreFile> storeFiles = new ArrayList<>(); + storeFiles.add(createFile(Environment.getDataMiscCeDirectory(userId), + STORE_FILE_USER_GENERAL)); + storeFiles.add(createFile(Environment.getDataMiscCeDirectory(userId), + STORE_FILE_USER_NETWORK_SUGGESTIONS)); + return storeFiles; } /** @@ -234,7 +310,29 @@ public class WifiConfigStore { * @return true if any of the store file is present, false otherwise. */ public boolean areStoresPresent() { - return (mSharedStore.exists() || (mUserStore != null && mUserStore.exists())); + // Checking for the shared store file existence is sufficient since this is guaranteed + // to be present on migrated devices. + return mSharedStore.exists(); + } + + /** + * Retrieve the list of {@link StoreData} instances registered for the provided + * {@link StoreFile}. + */ + private List<StoreData> retrieveStoreDataListForStoreFile(@NonNull StoreFile storeFile) { + return mStoreDataList + .stream() + .filter(s -> s.getStoreFileId() == storeFile.mFileId) + .collect(Collectors.toList()); + } + + /** + * Check if any of the provided list of {@link StoreData} instances registered + * for the provided {@link StoreFile }have indicated that they have new data to serialize. + */ + private boolean hasNewDataToSerialize(@NonNull StoreFile storeFile) { + List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile); + return storeDataList.stream().anyMatch(s -> s.hasNewDataToSerialize()); } /** @@ -247,50 +345,62 @@ public class WifiConfigStore { */ public void write(boolean forceSync) throws XmlPullParserException, IOException { + boolean hasAnyNewData = false; // Serialize the provided data and send it to the respective stores. The actual write will // be performed later depending on the |forceSync| flag . - byte[] sharedDataBytes = serializeData(true); - mSharedStore.storeRawDataToWrite(sharedDataBytes); - if (mUserStore != null) { - byte[] userDataBytes = serializeData(false); - mUserStore.storeRawDataToWrite(userDataBytes); + if (hasNewDataToSerialize(mSharedStore)) { + byte[] sharedDataBytes = serializeData(mSharedStore); + mSharedStore.storeRawDataToWrite(sharedDataBytes); + hasAnyNewData = true; + } + if (mUserStores != null) { + for (StoreFile userStoreFile : mUserStores) { + if (hasNewDataToSerialize(userStoreFile)) { + byte[] userDataBytes = serializeData(userStoreFile); + userStoreFile.storeRawDataToWrite(userDataBytes); + hasAnyNewData = true; + } + } } - // Every write provides a new snapshot to be persisted, so |forceSync| flag overrides any - // pending buffer writes. - if (forceSync) { + if (hasAnyNewData) { + // Every write provides a new snapshot to be persisted, so |forceSync| flag overrides + // any pending buffer writes. + if (forceSync) { + writeBufferedData(); + } else { + startBufferedWriteAlarm(); + } + } else if (forceSync && mBufferedWritePending) { + // no new data to write, but there is a pending buffered write. So, |forceSync| should + // flush that out. writeBufferedData(); - } else { - startBufferedWriteAlarm(); } } /** - * Serialize share data or user data from all store data. + * Serialize all the data from all the {@link StoreData} clients registered for the provided + * {@link StoreFile}. * - * @param shareData Flag indicating share data + * @param storeFile StoreFile that we want to write to. * @return byte[] of serialized bytes * @throws XmlPullParserException * @throws IOException */ - private byte[] serializeData(boolean shareData) throws XmlPullParserException, IOException { + private byte[] serializeData(@NonNull StoreFile storeFile) + throws XmlPullParserException, IOException { + List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile); + final XmlSerializer out = new FastXmlSerializer(); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); out.setOutput(outputStream, StandardCharsets.UTF_8.name()); XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER); XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION); - - for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) { - String tag = entry.getKey(); - StoreData storeData = entry.getValue(); - // Ignore this store data if this is for share file and the store data doesn't support - // share store. - if (shareData && !storeData.supportShareData()) { - continue; - } + for (StoreData storeData : storeDataList) { + String tag = storeData.getName(); XmlUtil.writeNextSectionStart(out, tag); - storeData.serializeData(out, shareData); + storeData.serializeData(out); XmlUtil.writeNextSectionEnd(out, tag); } XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER); @@ -329,8 +439,10 @@ public class WifiConfigStore { long writeStartTime = mClock.getElapsedSinceBootMillis(); mSharedStore.writeBufferedRawData(); - if (mUserStore != null) { - mUserStore.writeBufferedRawData(); + if (mUserStores != null) { + for (StoreFile userStoreFile : mUserStores) { + userStoreFile.writeBufferedRawData(); + } } long writeTime = mClock.getElapsedSinceBootMillis() - writeStartTime; @@ -344,80 +456,87 @@ public class WifiConfigStore { */ public void read() throws XmlPullParserException, IOException { // Reset both share and user store data. - resetStoreData(true); - if (mUserStore != null) { - resetStoreData(false); + resetStoreData(mSharedStore); + if (mUserStores != null) { + for (StoreFile userStoreFile : mUserStores) { + resetStoreData(userStoreFile); + } } long readStartTime = mClock.getElapsedSinceBootMillis(); byte[] sharedDataBytes = mSharedStore.readRawData(); - byte[] userDataBytes = null; - if (mUserStore != null) { - userDataBytes = mUserStore.readRawData(); + deserializeData(sharedDataBytes, mSharedStore); + if (mUserStores != null) { + for (StoreFile userStoreFile : mUserStores) { + byte[] userDataBytes = userStoreFile.readRawData(); + deserializeData(userDataBytes, userStoreFile); + } } long readTime = mClock.getElapsedSinceBootMillis() - readStartTime; - Log.d(TAG, "Reading from stores completed in " + readTime + " ms."); - deserializeData(sharedDataBytes, true); - if (mUserStore != null) { - deserializeData(userDataBytes, false); - } + Log.d(TAG, "Reading from all stores completed in " + readTime + " ms."); } /** - * Handles a user switch. This method changes the user specific store file and reads from the - * new user's store file. + * Handles a user switch. This method changes the user specific store files and reads from the + * new user's store files. * - * @param userStore StoreFile instance pointing to the user specific store file. This should - * be retrieved using {@link #createUserFile(int)} method. + * @param userStores List of {@link StoreFile} created using {@link #createUserFiles(int)}. */ - public void switchUserStoreAndRead(StoreFile userStore) + public void switchUserStoresAndRead(@NonNull List<StoreFile> userStores) throws XmlPullParserException, IOException { // Reset user store data. - resetStoreData(false); + if (mUserStores != null) { + for (StoreFile userStoreFile : mUserStores) { + resetStoreData(userStoreFile); + } + } // Stop any pending buffered writes, if any. stopBufferedWriteAlarm(); - mUserStore = userStore; + mUserStores = userStores; // Now read from the user store file. long readStartTime = mClock.getElapsedSinceBootMillis(); - byte[] userDataBytes = mUserStore.readRawData(); + for (StoreFile userStoreFile : mUserStores) { + byte[] userDataBytes = userStoreFile.readRawData(); + deserializeData(userDataBytes, userStoreFile); + } long readTime = mClock.getElapsedSinceBootMillis() - readStartTime; - Log.d(TAG, "Reading from user store completed in " + readTime + " ms."); - deserializeData(userDataBytes, false); + Log.d(TAG, "Reading from user stores completed in " + readTime + " ms."); } /** - * Reset share data or user data in all store data. - * - * @param shareData Flag indicating share data + * Reset data for all {@link StoreData} instances registered for this {@link StoreFile}. */ - private void resetStoreData(boolean shareData) { - for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) { - entry.getValue().resetData(shareData); + private void resetStoreData(@NonNull StoreFile storeFile) { + for (StoreData storeData: retrieveStoreDataListForStoreFile(storeFile)) { + storeData.resetData(); } } // Inform all the provided store data clients that there is nothing in the store for them. - private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet, boolean shareData) + private void indicateNoDataForStoreDatas(Collection<StoreData> storeDataSet) throws XmlPullParserException, IOException { for (StoreData storeData : storeDataSet) { - storeData.deserializeData(null, 0, shareData); + storeData.deserializeData(null, 0); } } /** - * Deserialize share data or user data into store data. + * Deserialize data from a {@link StoreFile} for all {@link StoreData} instances registered. * * @param dataBytes The data to parse - * @param shareData The flag indicating share data + * @param storeFile StoreFile that we read from. Will be used to retrieve the list of clients + * who have data to deserialize from this file. + * * @throws XmlPullParserException * @throws IOException */ - private void deserializeData(byte[] dataBytes, boolean shareData) + private void deserializeData(@NonNull byte[] dataBytes, @NonNull StoreFile storeFile) throws XmlPullParserException, IOException { + List<StoreData> storeDataList = retrieveStoreDataListForStoreFile(storeFile); if (dataBytes == null) { - indicateNoDataForStoreDatas(mStoreDataList.values(), shareData); + indicateNoDataForStoreDatas(storeDataList); return; } final XmlPullParser in = Xml.newPullParser(); @@ -431,18 +550,24 @@ public class WifiConfigStore { String[] headerName = new String[1]; Set<StoreData> storeDatasInvoked = new HashSet<>(); while (XmlUtil.gotoNextSectionOrEnd(in, headerName, rootTagDepth)) { - StoreData storeData = mStoreDataList.get(headerName[0]); + // There can only be 1 store data matching the tag (O indicates a fatal + // error). + StoreData storeData = storeDataList.stream() + .filter(s -> s.getName().equals(headerName[0])) + .findAny() + .orElse(null); if (storeData == null) { - throw new XmlPullParserException("Unknown store data: " + headerName[0]); + throw new XmlPullParserException("Unknown store data: " + headerName[0] + + ". List of store data: " + storeDataList); } - storeData.deserializeData(in, rootTagDepth + 1, shareData); + storeData.deserializeData(in, rootTagDepth + 1); storeDatasInvoked.add(storeData); } // Inform all the other registered store data clients that there is nothing in the store // for them. - Set<StoreData> storeDatasNotInvoked = new HashSet<>(mStoreDataList.values()); + Set<StoreData> storeDatasNotInvoked = new HashSet<>(storeDataList); storeDatasNotInvoked.removeAll(storeDatasInvoked); - indicateNoDataForStoreDatas(storeDatasNotInvoked, shareData); + indicateNoDataForStoreDatas(storeDatasNotInvoked); } /** @@ -464,6 +589,24 @@ public class WifiConfigStore { } /** + * Dump the local log buffer and other internal state of WifiConfigManager. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Dump of WifiConfigStore"); + pw.println("WifiConfigStore - Store Data Begin ----"); + for (StoreData storeData : mStoreDataList) { + pw.print("StoreData =>"); + pw.print(" "); + pw.print("Name: " + storeData.getName()); + pw.print(", "); + pw.println("File Id: " + storeData.getStoreFileId()); + pw.print(", "); + pw.println("File Name: " + STORE_ID_TO_FILE_NAME.get(storeData.getStoreFileId())); + } + pw.println("WifiConfigStore - Store Data End ----"); + } + + /** * Class to encapsulate all file writes. This is a wrapper over {@link AtomicFile} to write/read * raw data from the persistent file. This class provides helper methods to read/write the * entire file into a byte array. @@ -486,10 +629,15 @@ public class WifiConfigStore { * Store the file name for setting the file permissions/logging purposes. */ private String mFileName; + /** + * {@link StoreFileId} Type of store file. + */ + private @StoreFileId int mFileId; - public StoreFile(File file) { + public StoreFile(File file, @StoreFileId int fileId) { mAtomicFile = new AtomicFile(file); mFileName = mAtomicFile.getBaseFile().getAbsolutePath(); + mFileId = fileId; } /** @@ -562,48 +710,44 @@ public class WifiConfigStore { * The module will be responsible for serializing/deserializing their own data. * Whenever {@link WifiConfigStore#read()} is invoked, all registered StoreData instances will * be notified that a read was performed via {@link StoreData#deserializeData( - * XmlPullParser, int, boolean)} regardless of whether there is any data for them or not in the + * XmlPullParser, int)} regardless of whether there is any data for them or not in the * store file. * * Note: StoreData clients that need a config store read to kick-off operations should wait - * for the {@link StoreData#deserializeData(XmlPullParser, int, boolean)} invocation. + * for the {@link StoreData#deserializeData(XmlPullParser, int)} invocation. */ public interface StoreData { /** - * Serialize a XML data block to the output stream. The |shared| flag indicates if the - * output stream is backed by a share store or an user store. + * Serialize a XML data block to the output stream. * * @param out The output stream to serialize the data to - * @param shared Flag indicating if the output stream is backed by a share store or an - * user store */ - void serializeData(XmlSerializer out, boolean shared) + void serializeData(XmlSerializer out) throws XmlPullParserException, IOException; /** - * Deserialize a XML data block from the input stream. The |shared| flag indicates if the - * input stream is backed by a share store or an user store. When |shared| is set to true, - * the shared configuration data will be overwritten by the parsed data. Otherwise, - * the user configuration will be overwritten by the parsed data. + * Deserialize a XML data block from the input stream. * * @param in The input stream to read the data from. This could be null if there is * nothing in the store. * @param outerTagDepth The depth of the outer tag in the XML document - * @Param shared Flag indicating if the input stream is backed by a share store or an - * user store - * Note: This will be invoked every time a store file is read. For example: clients - * will get 2 invocations on bootup, one for shared store file (shared=True) & - * one for user store file (shared=False). + * Note: This will be invoked every time a store file is read, even if there is nothing + * in the store for them. */ - void deserializeData(@Nullable XmlPullParser in, int outerTagDepth, boolean shared) + void deserializeData(@Nullable XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException; /** - * Reset configuration data. The |shared| flag indicates which configuration data to - * reset. When |shared| is set to true, the shared configuration data will be reset. - * Otherwise, the user configuration data will be reset. + * Reset configuration data. + */ + void resetData(); + + /** + * Check if there is any new data to persist from the last write. + * + * @return true if the module has new data to persist, false otherwise. */ - void resetData(boolean shared); + boolean hasNewDataToSerialize(); /** * Return the name of this store data. The data will be enclosed under this tag in @@ -614,10 +758,17 @@ public class WifiConfigStore { String getName(); /** - * Flag indicating if shared configuration data is supported. + * File Id where this data needs to be written to. + * This should be one of {@link #STORE_FILE_SHARED_GENERAL}, + * {@link #STORE_FILE_USER_GENERAL} or + * {@link #STORE_FILE_USER_NETWORK_SUGGESTIONS}. * - * @return true if shared configuration data is supported + * Note: For most uses, the shared or user general store is sufficient. Creating and + * managing store files are expensive. Only use specific store files if you have a large + * amount of data which may not need to be persisted frequently (or at least not as + * frequently as the general store). + * @return Id of the file where this data needs to be persisted. */ - boolean supportShareData(); + @StoreFileId int getStoreFileId(); } } |