Index: src/org/openstreetmap/josm/data/ImageData.java
===================================================================
--- src/org/openstreetmap/josm/data/ImageData.java	(revision 15949)
+++ src/org/openstreetmap/josm/data/ImageData.java	(working copy)
@@ -4,6 +4,7 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -16,7 +17,7 @@
  * Class to hold {@link ImageEntry} and the current selection
  * @since 14590
  */
-public class ImageData {
+public class ImageData implements Data {
     /**
      * A listener that is informed when the current selection change
      */
@@ -343,4 +344,9 @@
     public void removeImageDataUpdateListener(ImageDataUpdateListener listener) {
         listeners.removeListener(listener);
     }
+
+    @Override
+    public Collection<DataSource> getDataSources() {
+        return Collections.emptyList();
+    }
 }
Index: src/org/openstreetmap/josm/data/osm/NoteData.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/NoteData.java	(revision 15949)
+++ src/org/openstreetmap/josm/data/osm/NoteData.java	(working copy)
@@ -9,6 +9,8 @@
 import java.util.List;
 import java.util.Map;
 
+import org.openstreetmap.josm.data.Data;
+import org.openstreetmap.josm.data.DataSource;
 import org.openstreetmap.josm.data.UserIdentityManager;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.notes.Note;
@@ -20,7 +22,7 @@
 /**
  * Class to hold and perform operations on a set of notes
  */
-public class NoteData {
+public class NoteData implements Data {
 
     /**
      * A listener that can be informed on note data changes.
@@ -59,6 +61,7 @@
 
     /**
      * Construct a new note container with a given list of notes
+     * 
      * @param notes The list of notes to populate the container with
      */
     public NoteData(Collection<Note> notes) {
@@ -140,7 +143,9 @@
     }
 
     /**
-     * Add notes to the data set. It only adds a note if the ID is not already present
+     * Add notes to the data set. It only adds a note if the ID is not already
+     * present
+     * 
      * @param newNotes A list of notes to add
      */
     public synchronized void addNotes(Collection<Note> newNotes) {
@@ -309,4 +314,9 @@
     public void removeNoteDataUpdateListener(NoteDataUpdateListener listener) {
         listeners.removeListener(listener);
     }
+
+    @Override
+    public Collection<DataSource> getDataSources() {
+        return Collections.emptyList(); // Notes don't currently store data sources
+    }
 }
Index: src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/AbstractModifiableLayer.java	(working copy)
@@ -1,6 +1,10 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.layer;
 
+import java.io.File;
+import java.io.IOException;
+
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.osm.Lockable;
 import org.openstreetmap.josm.gui.io.AbstractIOTask;
 import org.openstreetmap.josm.gui.io.AbstractUploadDialog;
@@ -102,4 +106,25 @@
         // Override if needed
         return false;
     }
+
+    /**
+     * Perform the autosave action for the layer
+     *
+     * @param file The file to save to
+     * @return {@code true} if the layer was successfully saved
+     * @throws IOException If there was an IO exception from saving
+     * @since xxx
+     */
+    public boolean autosave(File file) throws IOException {
+        // Override if needed;
+        return false;
+    }
+
+    /**
+     * Get the data for the modifiable layer
+     *
+     * @return The data object
+     * @since xxx
+     */
+    public abstract Data getData();
 }
Index: src/org/openstreetmap/josm/gui/layer/AutosaveTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(working copy)
@@ -31,7 +31,7 @@
 import java.util.regex.Pattern;
 
 import org.openstreetmap.josm.actions.OpenFileAction.OpenFileTask;
-import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.osm.NoteData;
 import org.openstreetmap.josm.data.osm.NoteData.NoteDataUpdateListener;
 import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
@@ -41,9 +41,7 @@
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.Notification;
-import org.openstreetmap.josm.gui.io.importexport.NoteExporter;
 import org.openstreetmap.josm.gui.io.importexport.NoteImporter;
-import org.openstreetmap.josm.gui.io.importexport.OsmExporter;
 import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
@@ -116,8 +114,7 @@
     }
 
     private final DataSetListenerAdapter datasetAdapter = new DataSetListenerAdapter(this);
-    private final Set<DataSet> changedDatasets = new HashSet<>();
-    private final Set<NoteData> changedNoteData = new HashSet<>();
+    private final Set<Data> changedData = new HashSet<>();
     private final List<AutosaveLayerInfo<?>> layersInfo = new ArrayList<>();
     private final Object layersLock = new Object();
     private final Deque<File> deletedLayers = new LinkedList<>();
@@ -125,7 +122,28 @@
     private final File autosaveDir = new File(Config.getDirs().getUserDataDirectory(true), AUTOSAVE_DIR);
     private final File deletedLayersDir = new File(Config.getDirs().getUserDataDirectory(true), DELETED_LAYERS_DIR);
 
+    private static AutosaveTask task;
+
     /**
+     * Initialize the AutosaveTask and set the global instance, if the global
+     * instance is not yet set.
+     */
+    public AutosaveTask() {
+        if (task == null)
+            task = this;
+    }
+
+    /**
+     * @return The global instance for the AutosaveTask
+     * @since xxx
+     */
+    public static AutosaveTask getInstance() {
+        if (task == null)
+            new AutosaveTask();
+        return task;
+    }
+
+    /**
      * Replies the autosave directory.
      * @return the autosave directory
      * @since 10299
@@ -236,24 +254,12 @@
             info.layerName = info.layer.getName();
         }
         try {
-            if (info.layer instanceof OsmDataLayer) {
-                OsmDataLayer dataLayer = (OsmDataLayer) info.layer;
-                if (changedDatasets.remove(dataLayer.data)) {
-                    File file = getNewLayerFile(info, new Date(), 0);
-                    if (file != null) {
-                        info.backupFiles.add(file);
-                        new OsmExporter().exportData(file, info.layer, true /* no backup with appended ~ */);
-                    }
+            if (changedData.remove(info.layer.getData())) {
+                File file = getNewLayerFile(info, new Date(), 0);
+                if (file != null) {
+                    info.backupFiles.add(file);
+                    info.layer.autosave(file);
                 }
-            } else if (info.layer instanceof NoteLayer) {
-                NoteLayer noteLayer = (NoteLayer) info.layer;
-                if (changedNoteData.remove(noteLayer.getNoteData())) {
-                    File file = getNewLayerFile(info, new Date(), 0);
-                    if (file != null) {
-                        info.backupFiles.add(file);
-                        new NoteExporter().exportData(file, info.layer);
-                    }
-                }
             }
         } catch (IOException e) {
             Logging.error(e);
@@ -273,8 +279,7 @@
                 for (AutosaveLayerInfo<?> info: layersInfo) {
                     savelayer(info);
                 }
-                changedDatasets.clear();
-                changedNoteData.clear();
+                changedData.clear();
                 if (PROP_NOTIFICATION.get() && !layersInfo.isEmpty()) {
                     GuiHelper.runInEDT(this::displayNotification);
                 }
@@ -317,6 +322,10 @@
             registerNewlayer((OsmDataLayer) e.getAddedLayer());
         } else if (e.getAddedLayer() instanceof NoteLayer) {
             registerNewlayer((NoteLayer) e.getAddedLayer());
+        } else if (e.getAddedLayer() instanceof AbstractModifiableLayer) {
+            synchronized (layersLock) {
+                layersInfo.add(new AutosaveLayerInfo<>((AbstractModifiableLayer) e.getAddedLayer()));
+            }
         }
     }
 
@@ -361,14 +370,25 @@
 
     @Override
     public void processDatasetEvent(AbstractDatasetChangedEvent event) {
-        changedDatasets.add(event.getDataset());
+        changedData.add(event.getDataset());
     }
 
     @Override
     public void noteDataUpdated(NoteData data) {
-        changedNoteData.add(data);
+        changedData.add(data);
     }
 
+    /**
+     * Indicate that data has changed, and it might be a good idea to autosave.
+     *
+     * @param data The data that has changed
+     * @return See {@link Set#add}
+     * @since xxx
+     */
+    public boolean dataUpdated(Data data) {
+        return changedData.add(data);
+    }
+
     @Override
     public void selectedNoteChanged(NoteData noteData) {
         // Do nothing
Index: src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(working copy)
@@ -27,6 +27,7 @@
 import org.openstreetmap.josm.actions.RenameLayerAction;
 import org.openstreetmap.josm.actions.SaveActionBase;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.SystemOfMeasurement;
 import org.openstreetmap.josm.data.gpx.GpxConstants;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -546,4 +547,9 @@
         // no i18n for international values
         return isLocalFile ? "survey" : null;
     }
+
+    @Override
+    public Data getData() {
+        return data;
+    }
 }
Index: src/org/openstreetmap/josm/gui/layer/NoteLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/NoteLayer.java	(working copy)
@@ -13,6 +13,7 @@
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
 import java.io.File;
+import java.io.IOException;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -35,6 +36,7 @@
 
 import org.openstreetmap.josm.actions.SaveActionBase;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.notes.Note;
 import org.openstreetmap.josm.data.notes.Note.State;
 import org.openstreetmap.josm.data.notes.NoteComment;
@@ -91,8 +93,9 @@
 
     /**
      * Create a new note layer with a set of notes
-     * @param notes A list of notes to show in this layer
-     * @param name The name of the layer. Typically "Notes"
+     *
+     * @param notes  A list of notes to show in this layer
+     * @param name   The name of the layer. Typically "Notes"
      */
     public NoteLayer(Collection<Note> notes, String name) {
         this(new NoteData(notes), name);
@@ -478,4 +481,15 @@
     public String getChangesetSourceTag() {
         return "Notes";
     }
+
+    @Override
+    public boolean autosave(File file) throws IOException {
+        new NoteExporter().exportData(file, this);
+        return true;
+    }
+
+    @Override
+    public Data getData() {
+        return getNoteData();
+    }
 }
Index: src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(working copy)
@@ -52,6 +52,7 @@
 import org.openstreetmap.josm.actions.ToggleUploadDiscouragedLayerAction;
 import org.openstreetmap.josm.data.APIDataSet;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.DataSource;
 import org.openstreetmap.josm.data.ProjectionBounds;
 import org.openstreetmap.josm.data.UndoRedoHandler;
@@ -110,6 +111,7 @@
 import org.openstreetmap.josm.gui.io.UploadDialog;
 import org.openstreetmap.josm.gui.io.UploadLayerTask;
 import org.openstreetmap.josm.gui.io.importexport.NoteExporter;
+import org.openstreetmap.josm.gui.io.importexport.OsmExporter;
 import org.openstreetmap.josm.gui.io.importexport.OsmImporter;
 import org.openstreetmap.josm.gui.io.importexport.ValidatorErrorExporter;
 import org.openstreetmap.josm.gui.io.importexport.WMSLayerImporter;
@@ -1335,4 +1337,15 @@
     public boolean isUploadInProgress() {
         return isUploadInProgress.get();
     }
+
+    @Override
+    public Data getData() {
+        return getDataSet();
+    }
+
+    @Override
+    public boolean autosave(File file) throws IOException {
+        new OsmExporter().exportData(file, this, true /* no backup with appended ~ */);
+        return true;
+    }
 }
Index: src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 15949)
+++ src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(working copy)
@@ -36,10 +36,11 @@
 import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.actions.mapmode.SelectLassoAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.actions.mapmode.SelectAction;
+import org.openstreetmap.josm.actions.mapmode.SelectLassoAction;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.Data;
 import org.openstreetmap.josm.data.ImageData;
 import org.openstreetmap.josm.data.ImageData.ImageDataUpdateListener;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -1015,4 +1016,9 @@
     public String getChangesetSourceTag() {
         return "Geotagged Images";
     }
+
+    @Override
+    public Data getData() {
+        return data;
+    }
 }
