Merge "Extract DDMS PreferenceStore."
diff --git a/ddms/app/src/com/android/ddms/Main.java b/ddms/app/src/com/android/ddms/Main.java
index b538ba9..14ef910 100644
--- a/ddms/app/src/com/android/ddms/Main.java
+++ b/ddms/app/src/com/android/ddms/Main.java
@@ -76,14 +76,13 @@
 
         // if this is the first time using ddms or adt, open up the stats service
         // opt out dialog, and request user for permissions.
-        if (!SdkStatsService.pingPermissionsSet()) {
-            SdkStatsService.getUserPermissionForPing(new Shell(Display.getDefault()));
-        }
+        SdkStatsService stats = new SdkStatsService();
+        stats.checkUserPermissionForPing(new Shell(Display.getDefault()));
 
         // the "ping" argument means to check in with the server and exit
         // the application name and version number must also be supplied
         if (args.length >= 3 && args[0].equals("ping")) {
-            SdkStatsService.ping(args[1], args[2]);
+            stats.ping(args[1], args[2]);
             return;
         } else if (args.length > 0) {
             Log.e("ddms", "Unknown argument: " + args[0]);
@@ -101,7 +100,8 @@
 
         // we're past the point where ddms can be called just to send a ping, so we can
         // ping for ddms itself.
-        ping(ddmsParentLocation);
+        ping(stats, ddmsParentLocation);
+        stats = null;
 
         DebugPortManager.setProvider(DebugPortProvider.getInstance());
 
@@ -131,7 +131,7 @@
         return System.getProperty("os.name").startsWith("Mac OS"); //$NON-NLS-1$ //$NON-NLS-2$
     }
 
-    private static void ping(String ddmsParentLocation) {
+    private static void ping(SdkStatsService stats, String ddmsParentLocation) {
         Properties p = new Properties();
         try{
             File sourceProp;
@@ -143,7 +143,7 @@
             p.load(new FileInputStream(sourceProp));
             sRevision = p.getProperty("Pkg.Revision"); //$NON-NLS-1$
             if (sRevision != null && sRevision.length() > 0) {
-                SdkStatsService.ping("ddms", sRevision);  //$NON-NLS-1$
+                stats.ping("ddms", sRevision);  //$NON-NLS-1$
             }
         } catch (FileNotFoundException e) {
             // couldn't find the file? don't ping.
diff --git a/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java
index 4745ed7..0669d7c 100644
--- a/ddms/app/src/com/android/ddms/PrefsDialog.java
+++ b/ddms/app/src/com/android/ddms/PrefsDialog.java
@@ -1,19 +1,18 @@
-/* //device/tools/ddms/src/com/android/ddms/PrefsDialog.java
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package com.android.ddms;
 
@@ -25,8 +24,8 @@
 import com.android.ddmuilib.PortFieldEditor;
 import com.android.ddmuilib.logcat.LogCatMessageList;
 import com.android.ddmuilib.logcat.LogCatPanel;
+import com.android.sdkstats.DdmsPreferenceStore;
 import com.android.sdkstats.SdkStatsPermissionDialog;
-import com.android.sdkstats.SdkStatsService;
 
 import org.eclipse.jface.preference.BooleanFieldEditor;
 import org.eclipse.jface.preference.DirectoryFieldEditor;
@@ -63,9 +62,6 @@
  */
 public final class PrefsDialog {
 
-    // Preference store.
-    private static PreferenceStore mPrefStore;
-
     // public const values for storage
     public final static String SHELL_X = "shellX"; //$NON-NLS-1$
     public final static String SHELL_Y = "shellY"; //$NON-NLS-1$
@@ -94,6 +90,8 @@
     private final static String PREFS_USE_ADBHOST = "useAdbHost"; //$NON-NLS-1$
     private final static String PREFS_ADBHOST_VALUE = "adbHostValue"; //$NON-NLS-1$
 
+    // Preference store.
+    private static DdmsPreferenceStore mStore = new DdmsPreferenceStore();
 
     /**
      * Private constructor -- do not instantiate.
@@ -102,17 +100,23 @@
 
     /**
      * Return the PreferenceStore that holds our values.
+     *
+     * @deprecated Callers should use {@link DdmsPreferenceStore} directly.
      */
+    @Deprecated
     public static PreferenceStore getStore() {
-        return mPrefStore;
+        return mStore.getPreferenceStore();
     }
 
     /**
      * Save the prefs to the config file.
+     *
+     * @deprecated Callers should use {@link DdmsPreferenceStore} directly.
      */
+    @Deprecated
     public static void save() {
         try {
-            mPrefStore.save();
+            mStore.getPreferenceStore().save();
         }
         catch (IOException ioe) {
             Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage());
@@ -133,11 +137,9 @@
      * the second part later on for the "changed" events.
      */
     public static void init() {
-        assert mPrefStore == null;
+        PreferenceStore prefStore = mStore.getPreferenceStore();
 
-        mPrefStore = SdkStatsService.getPreferenceStore();
-
-        if (mPrefStore == null) {
+        if (prefStore == null) {
             // we have a serious issue here...
             Log.e("ddms",
                     "failed to access both the user HOME directory and the system wide temp folder. Quitting.");
@@ -148,20 +150,20 @@
         setDefaults(System.getProperty("user.home")); //$NON-NLS-1$
 
         // listen for changes
-        mPrefStore.addPropertyChangeListener(new ChangeListener());
+        prefStore.addPropertyChangeListener(new ChangeListener());
 
         // Now we initialize the value of the preference, from the values in the store.
 
         // First the ddm lib.
-        DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE));
-        DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT));
-        DdmPreferences.setLogLevel(mPrefStore.getString(PREFS_LOG_LEVEL));
-        DdmPreferences.setInitialThreadUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_THREAD_UPDATE));
-        DdmPreferences.setInitialHeapUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_HEAP_UPDATE));
-        DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT));
-        DdmPreferences.setProfilerBufferSizeMb(mPrefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB));
-        DdmPreferences.setUseAdbHost(mPrefStore.getBoolean(PREFS_USE_ADBHOST));
-        DdmPreferences.setAdbHostValue(mPrefStore.getString(PREFS_ADBHOST_VALUE));
+        DdmPreferences.setDebugPortBase(prefStore.getInt(PREFS_DEBUG_PORT_BASE));
+        DdmPreferences.setSelectedDebugPort(prefStore.getInt(PREFS_SELECTED_DEBUG_PORT));
+        DdmPreferences.setLogLevel(prefStore.getString(PREFS_LOG_LEVEL));
+        DdmPreferences.setInitialThreadUpdate(prefStore.getBoolean(PREFS_DEFAULT_THREAD_UPDATE));
+        DdmPreferences.setInitialHeapUpdate(prefStore.getBoolean(PREFS_DEFAULT_HEAP_UPDATE));
+        DdmPreferences.setTimeOut(prefStore.getInt(PREFS_TIMEOUT));
+        DdmPreferences.setProfilerBufferSizeMb(prefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB));
+        DdmPreferences.setUseAdbHost(prefStore.getBoolean(PREFS_USE_ADBHOST));
+        DdmPreferences.setAdbHostValue(prefStore.getString(PREFS_ADBHOST_VALUE));
 
         // some static values
         String out = System.getenv("ANDROID_PRODUCT_OUT"); //$NON-NLS-1$
@@ -177,8 +179,8 @@
         DdmUiPreferences.setTraceviewLocation(traceview);
 
         // Now the ddmui lib
-        DdmUiPreferences.setStore(mPrefStore);
-        DdmUiPreferences.setThreadRefreshInterval(mPrefStore.getInt(PREFS_THREAD_REFRESH_INTERVAL));
+        DdmUiPreferences.setStore(prefStore);
+        DdmUiPreferences.setThreadRefreshInterval(prefStore.getInt(PREFS_THREAD_REFRESH_INTERVAL));
     }
 
     /*
@@ -191,42 +193,44 @@
      * class.getInstance().
      */
     private static void setDefaults(String homeDir) {
-        mPrefStore.setDefault(PREFS_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE);
+        PreferenceStore prefStore = mStore.getPreferenceStore();
 
-        mPrefStore.setDefault(PREFS_SELECTED_DEBUG_PORT,
+        prefStore.setDefault(PREFS_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE);
+
+        prefStore.setDefault(PREFS_SELECTED_DEBUG_PORT,
                 DdmPreferences.DEFAULT_SELECTED_DEBUG_PORT);
 
-        mPrefStore.setDefault(PREFS_USE_ADBHOST, DdmPreferences.DEFAULT_USE_ADBHOST);
-        mPrefStore.setDefault(PREFS_ADBHOST_VALUE, DdmPreferences.DEFAULT_ADBHOST_VALUE);
+        prefStore.setDefault(PREFS_USE_ADBHOST, DdmPreferences.DEFAULT_USE_ADBHOST);
+        prefStore.setDefault(PREFS_ADBHOST_VALUE, DdmPreferences.DEFAULT_ADBHOST_VALUE);
 
-        mPrefStore.setDefault(PREFS_DEFAULT_THREAD_UPDATE, true);
-        mPrefStore.setDefault(PREFS_DEFAULT_HEAP_UPDATE, false);
-        mPrefStore.setDefault(PREFS_THREAD_REFRESH_INTERVAL,
+        prefStore.setDefault(PREFS_DEFAULT_THREAD_UPDATE, true);
+        prefStore.setDefault(PREFS_DEFAULT_HEAP_UPDATE, false);
+        prefStore.setDefault(PREFS_THREAD_REFRESH_INTERVAL,
             DdmUiPreferences.DEFAULT_THREAD_REFRESH_INTERVAL);
 
-        mPrefStore.setDefault("textSaveDir", homeDir); //$NON-NLS-1$
-        mPrefStore.setDefault("imageSaveDir", homeDir); //$NON-NLS-1$
+        prefStore.setDefault("textSaveDir", homeDir); //$NON-NLS-1$
+        prefStore.setDefault("imageSaveDir", homeDir); //$NON-NLS-1$
 
-        mPrefStore.setDefault(PREFS_LOG_LEVEL, "info"); //$NON-NLS-1$
+        prefStore.setDefault(PREFS_LOG_LEVEL, "info"); //$NON-NLS-1$
 
-        mPrefStore.setDefault(PREFS_TIMEOUT, DdmPreferences.DEFAULT_TIMEOUT);
-        mPrefStore.setDefault(PREFS_PROFILER_BUFFER_SIZE_MB,
+        prefStore.setDefault(PREFS_TIMEOUT, DdmPreferences.DEFAULT_TIMEOUT);
+        prefStore.setDefault(PREFS_PROFILER_BUFFER_SIZE_MB,
                 DdmPreferences.DEFAULT_PROFILER_BUFFER_SIZE_MB);
 
         // choose a default font for the text output
         FontData fdat = new FontData("Courier", 10, SWT.NORMAL); //$NON-NLS-1$
-        mPrefStore.setDefault("textOutputFont", fdat.toString()); //$NON-NLS-1$
+        prefStore.setDefault("textOutputFont", fdat.toString()); //$NON-NLS-1$
 
         // layout information.
-        mPrefStore.setDefault(SHELL_X, 100);
-        mPrefStore.setDefault(SHELL_Y, 100);
-        mPrefStore.setDefault(SHELL_WIDTH, 800);
-        mPrefStore.setDefault(SHELL_HEIGHT, 600);
+        prefStore.setDefault(SHELL_X, 100);
+        prefStore.setDefault(SHELL_Y, 100);
+        prefStore.setDefault(SHELL_WIDTH, 800);
+        prefStore.setDefault(SHELL_HEIGHT, 600);
 
-        mPrefStore.setDefault(EXPLORER_SHELL_X, 50);
-        mPrefStore.setDefault(EXPLORER_SHELL_Y, 50);
+        prefStore.setDefault(EXPLORER_SHELL_X, 50);
+        prefStore.setDefault(EXPLORER_SHELL_Y, 50);
 
-        mPrefStore.setDefault(SHOW_NATIVE_HEAP, false);
+        prefStore.setDefault(SHOW_NATIVE_HEAP, false);
     }
 
 
@@ -240,28 +244,29 @@
     private static class ChangeListener implements IPropertyChangeListener {
         public void propertyChange(PropertyChangeEvent event) {
             String changed = event.getProperty();
+            PreferenceStore prefStore = mStore.getPreferenceStore();
 
             if (changed.equals(PREFS_DEBUG_PORT_BASE)) {
-                DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE));
+                DdmPreferences.setDebugPortBase(prefStore.getInt(PREFS_DEBUG_PORT_BASE));
             } else if (changed.equals(PREFS_SELECTED_DEBUG_PORT)) {
-                DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT));
+                DdmPreferences.setSelectedDebugPort(prefStore.getInt(PREFS_SELECTED_DEBUG_PORT));
             } else if (changed.equals(PREFS_LOG_LEVEL)) {
                 DdmPreferences.setLogLevel((String)event.getNewValue());
             } else if (changed.equals("textSaveDir")) {
-                mPrefStore.setValue("lastTextSaveDir",
+                prefStore.setValue("lastTextSaveDir",
                     (String) event.getNewValue());
             } else if (changed.equals("imageSaveDir")) {
-                mPrefStore.setValue("lastImageSaveDir",
+                prefStore.setValue("lastImageSaveDir",
                     (String) event.getNewValue());
             } else if (changed.equals(PREFS_TIMEOUT)) {
-                DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT));
+                DdmPreferences.setTimeOut(prefStore.getInt(PREFS_TIMEOUT));
             } else if (changed.equals(PREFS_PROFILER_BUFFER_SIZE_MB)) {
                 DdmPreferences.setProfilerBufferSizeMb(
-                        mPrefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB));
+                        prefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB));
             } else if (changed.equals(PREFS_USE_ADBHOST)) {
-                DdmPreferences.setUseAdbHost(mPrefStore.getBoolean(PREFS_USE_ADBHOST));
+                DdmPreferences.setUseAdbHost(prefStore.getBoolean(PREFS_USE_ADBHOST));
             } else if (changed.equals(PREFS_ADBHOST_VALUE)) {
-                DdmPreferences.setAdbHostValue(mPrefStore.getString(PREFS_ADBHOST_VALUE));
+                DdmPreferences.setAdbHostValue(prefStore.getString(PREFS_ADBHOST_VALUE));
             } else {
                 Log.v("ddms", "Preference change: " + event.getProperty()
                     + ": '" + event.getOldValue()
@@ -275,7 +280,8 @@
      * Create and display the dialog.
      */
     public static void run(Shell shell) {
-        assert mPrefStore != null;
+        PreferenceStore prefStore = mStore.getPreferenceStore();
+        assert prefStore != null;
 
         PreferenceManager prefMgr = new PreferenceManager();
 
@@ -302,7 +308,7 @@
         prefMgr.addToRoot(node);
 
         PreferenceDialog dlg = new PreferenceDialog(shell, prefMgr);
-        dlg.setPreferenceStore(mPrefStore);
+        dlg.setPreferenceStore(prefStore);
 
         // run it
         try {
@@ -313,7 +319,7 @@
 
         // save prefs
         try {
-            mPrefStore.save();
+            prefStore.save();
         }
         catch (IOException ioe) {
         }
@@ -549,7 +555,7 @@
                 }
             });
 
-            mOptInCheckbox = new BooleanFieldEditor(SdkStatsService.PING_OPT_IN,
+            mOptInCheckbox = new BooleanFieldEditor(DdmsPreferenceStore.PING_OPT_IN,
                     SdkStatsPermissionDialog.CHECKBOX_TEXT, mTop);
             mOptInCheckbox.setPage(this);
             mOptInCheckbox.setPreferenceStore(getPreferenceStore());
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
index 7fb2294..c67afdb 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
@@ -232,9 +232,8 @@
 
         /* When the ADT preferences page is made visible, display the dialog to obtain
          * permissions for the ping service. */
-        if (!SdkStatsService.pingPermissionsSet()) {
-            Shell parent = getShell();
-            SdkStatsService.getUserPermissionForPing(parent);
-        }
+        SdkStatsService stats = new SdkStatsService();
+        Shell parent = getShell();
+        stats.checkUserPermissionForPing(parent);
     }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java
index 6c07fe9..a333bc5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.preferences;
 
+import com.android.sdkstats.DdmsPreferenceStore;
 import com.android.sdkstats.SdkStatsPermissionDialog;
 import com.android.sdkstats.SdkStatsService;
 
@@ -40,6 +41,7 @@
     private static final int WRAP_WIDTH_PX = 200;
 
     private BooleanFieldEditor mOptInCheckBox;
+    private DdmsPreferenceStore mStore = new DdmsPreferenceStore();
 
     public UsagePreferencePage() {
     }
@@ -73,10 +75,10 @@
             }
         });
 
-        mOptInCheckBox = new BooleanFieldEditor(SdkStatsService.PING_OPT_IN,
+        mOptInCheckBox = new BooleanFieldEditor(DdmsPreferenceStore.PING_OPT_IN,
                 SdkStatsPermissionDialog.CHECKBOX_TEXT, top);
         mOptInCheckBox.setPage(this);
-        mOptInCheckBox.setPreferenceStore(SdkStatsService.getPreferenceStore());
+        mOptInCheckBox.setPreferenceStore(mStore.getPreferenceStore());
         mOptInCheckBox.load();
 
         return top;
@@ -119,14 +121,6 @@
     }
 
     private void save() {
-        try {
-            PreferenceStore store = SdkStatsService.getPreferenceStore();
-            if (store !=  null) {
-                store.setValue(SdkStatsService.PING_OPT_IN, mOptInCheckBox.getBooleanValue());
-                store.save();
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        mStore.setPingOptIn(mOptInCheckBox.getBooleanValue());
     }
 }
diff --git a/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java b/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java
new file mode 100755
index 0000000..8875eb6
--- /dev/null
+++ b/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkstats;
+
+import com.android.prefs.AndroidLocation;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+
+import org.eclipse.jface.preference.PreferenceStore;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+
+/** Utility class to send "ping" usage reports to the server. */
+public class DdmsPreferenceStore {
+
+    public  final static String PING_OPT_IN = "pingOptIn";          //$NON-NLS-1$
+    private final static String PING_TIME   = "pingTime";           //$NON-NLS-1$
+    private final static String PING_ID     = "pingId";             //$NON-NLS-1$
+    private final static String ADT_STARTUP_WIZARD = "adtStrtWzd";  //$NON-NLS-1$
+    private final static String LAST_SDK_PATH = "lastSdkPath";      //$NON-NLS-1$
+
+    /**
+     * Lock for the preference store.
+     */
+    private static volatile Object[] sLock = new Object[0];
+    /**
+     * PreferenceStore for DDMS. Creation and usage must be synchronized on sLock.
+     * Don't use it directly, instead retrieve it via {@link #getPreferenceStore()}.
+     */
+    private static volatile PreferenceStore sPrefStore;
+
+    public DdmsPreferenceStore() {
+    }
+
+    /**
+     * Returns the DDMS {@link PreferenceStore}.
+     * This keeps a static reference on the store, so consequent calls will
+     * return always the same store.
+     */
+    public PreferenceStore getPreferenceStore() {
+        synchronized(sLock) {
+            if (sPrefStore == null) {
+                // get the location of the preferences
+                String homeDir = null;
+                try {
+                    homeDir = AndroidLocation.getFolder();
+                } catch (AndroidLocationException e1) {
+                    // pass, we'll do a dummy store since homeDir is null
+                }
+
+                if (homeDir == null) {
+                    sPrefStore = new PreferenceStore();
+                    return sPrefStore;
+                }
+
+                assert homeDir != null;
+
+                String rcFileName = homeDir + "ddms.cfg";                       //$NON-NLS-1$
+
+                // also look for an old pref file in the previous location
+                String oldPrefPath = System.getProperty("user.home")            //$NON-NLS-1$
+                    + File.separator + ".ddmsrc";                               //$NON-NLS-1$
+                File oldPrefFile = new File(oldPrefPath);
+                if (oldPrefFile.isFile()) {
+                    try {
+                        PreferenceStore oldStore = new PreferenceStore(oldPrefPath);
+                        oldStore.load();
+
+                        oldStore.save(new FileOutputStream(rcFileName), "");    //$NON-NLS-1$
+                        oldPrefFile.delete();
+
+                        PreferenceStore newStore = new PreferenceStore(rcFileName);
+                        newStore.load();
+                        sPrefStore = newStore;
+                    } catch (IOException e) {
+                        // create a new empty store.
+                        sPrefStore = new PreferenceStore(rcFileName);
+                    }
+                } else {
+                    sPrefStore = new PreferenceStore(rcFileName);
+
+                    try {
+                        sPrefStore.load();
+                    } catch (IOException e) {
+                        System.err.println("Error Loading DDMS Preferences");
+                    }
+                }
+            }
+
+            assert sPrefStore != null;
+            return sPrefStore;
+        }
+    }
+
+    /**
+     * Save the prefs to the config file.
+     */
+    public void save() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            try {
+                prefs.save();
+            }
+            catch (IOException ioe) {
+                // FIXME com.android.dmmlib.Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage());
+            }
+        }
+    }
+
+    // ---- Utility methods to access some specifis prefs ----
+
+    /**
+     * Indicates whether the ping ID is set.
+     * This should be true when {@link #isPingOptIn()} is true.
+     *
+     * @return true if a ping ID is set, which means the user gave permission
+     *              to use the ping service.
+     */
+    public boolean hasPingId() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            return prefs != null && prefs.contains(PING_ID);
+        }
+    }
+
+    /**
+     * Retrieves the current ping ID, if set.
+     * To know if the ping ID is set, use {@link #hasPingId()}.
+     * The  value returned is 0L if there's no store or no ping ID set in the store.
+     */
+    public long getPingId() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            return prefs == null ? 0L : prefs.getLong(PING_ID);
+        }
+    }
+
+    /**
+     * Generates a new random ping ID and saves it in the preference store.
+     *
+     * @return The new ping ID.
+     */
+    public long generateNewPingId() {
+        PreferenceStore prefs = getPreferenceStore();
+
+        Random rnd = new Random();
+        long id = rnd.nextLong();
+
+        // Let's try to be conservative and avoid 0L as an ID.
+        // (ideally it would be nice to keep 0L as a special reserved value,
+        //  to both have a default value and detect when variables aren't set
+        //  properly. It's too late to enforce this, but we can still avoid
+        //  generating new ones like this.)
+        for (int i = 0; id == 0L && i < 10; i++) {
+            id = rnd.nextLong();
+        }
+
+        synchronized(sLock) {
+            prefs.setValue(PING_ID, id);
+            try {
+                prefs.save();
+            } catch (IOException e) {
+                /* ignore exceptions while saving preferences */
+            }
+        }
+
+        return id;
+    }
+
+    /**
+     * Returns the "ping opt in" value from the preference store.
+     * This would be true if there's a valid preference store and
+     * the user opted for sending ping statistics.
+     */
+    public boolean isPingOptIn() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            return prefs != null && prefs.contains(PING_OPT_IN);
+        }
+    }
+
+    /**
+     * Saves the "ping opt in" value in the preference store.
+     *
+     * @param optIn The new user opt-in value.
+     */
+    public void setPingOptIn(boolean optIn) {
+        PreferenceStore prefs = getPreferenceStore();
+
+        synchronized(sLock) {
+            prefs.setValue(PING_OPT_IN, optIn);
+            try {
+                prefs.save();
+            } catch (IOException e) {
+                /* ignore exceptions while saving preferences */
+            }
+        }
+    }
+
+    /**
+     * Retrieves the ping time for the given app from the preference store.
+     * Callers should use {@link System#currentTimeMillis()} for time stamps.
+     *
+     * @param app The app name identifier.
+     * @return 0L if we don't have a preference store or there was no time
+     *  recorded in the store for the requested app. Otherwise the time stamp
+     *  from the store.
+     */
+    public long getPingTime(String app) {
+        PreferenceStore prefs = getPreferenceStore();
+        String timePref = PING_TIME + "." + app;  //$NON-NLS-1$
+        synchronized(sLock) {
+            return prefs == null ? 0 : prefs.getLong(timePref);
+        }
+    }
+
+    /**
+     * Sets the ping time for the given app from the preference store.
+     * Callers should use {@link System#currentTimeMillis()} for time stamps.
+     *
+     * @param app The app name identifier.
+     * @param timeStamp The time stamp from the store.
+     *                   0L is a sepcial value that should not be used.
+     */
+    public void setPingTime(String app, long timeStamp) {
+        PreferenceStore prefs = getPreferenceStore();
+        String timePref = PING_TIME + "." + app;  //$NON-NLS-1$
+        synchronized(sLock) {
+            prefs.setValue(timePref, timeStamp);
+            try {
+                prefs.save();
+            } catch (IOException ioe) {
+                /* ignore exceptions while saving preferences */
+            }
+        }
+    }
+
+    /**
+     * True if there's a valid preference store and the ADT startup
+     * wizard has been shown once.
+     */
+    public boolean wasAdtStartupWizardShown() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            return prefs == null ? false : prefs.getBoolean(ADT_STARTUP_WIZARD);
+        }
+    }
+
+    /**
+     * Sets whether the ADT startup wizard has been shown.
+     */
+    public void setAdtStartupWizardShown(boolean shown) {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            prefs.setValue(ADT_STARTUP_WIZARD, shown);
+            try {
+                prefs.save();
+            } catch (IOException ioe) {
+                /* ignore exceptions while saving preferences */
+            }
+        }
+    }
+
+    /**
+     * Retrieves the last SDK OS path.
+     * <p/>
+     * This is just an information value, the path may not exist, may not
+     * even be on an existing file system and/or may not point to an SDK
+     * anymore.
+     *
+     * @return The last SDK OS path from the preference store, or null if
+     *  there is no store or an empty string if it is not defined.
+     */
+    public String getLastSdkPath() {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            return prefs == null ? null : prefs.getString(LAST_SDK_PATH);
+        }
+    }
+
+    /**
+     * Sets the last SDK OS path.
+     *
+     * @param osSdkPath The SDK OS Path. Can be null or empty.
+     */
+    public void setLastSdkPath(String osSdkPath) {
+        PreferenceStore prefs = getPreferenceStore();
+        synchronized(sLock) {
+            prefs.setValue(LAST_SDK_PATH, osSdkPath);
+            try {
+                prefs.save();
+            } catch (IOException ioe) {
+                /* ignore exceptions while saving preferences */
+            }
+        }
+    }
+}
diff --git a/sdkstats/src/com/android/sdkstats/SdkStatsService.java b/sdkstats/src/com/android/sdkstats/SdkStatsService.java
index 25a7d97..b1b8c47 100644
--- a/sdkstats/src/com/android/sdkstats/SdkStatsService.java
+++ b/sdkstats/src/com/android/sdkstats/SdkStatsService.java
@@ -16,20 +16,13 @@
 
 package com.android.sdkstats;
 
-import com.android.prefs.AndroidLocation;
-import com.android.prefs.AndroidLocation.AndroidLocationException;
-
-import org.eclipse.jface.preference.PreferenceStore;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLEncoder;
-import java.util.Random;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -39,11 +32,10 @@
     /** Minimum interval between ping, in milliseconds. */
     private static final long PING_INTERVAL_MSEC = 86400 * 1000;  // 1 day
 
-    public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$
-    public final static String PING_TIME = "pingTime"; //$NON-NLS-1$
-    public final static String PING_ID = "pingId"; //$NON-NLS-1$
+    private DdmsPreferenceStore mStore = new DdmsPreferenceStore();
 
-    private static PreferenceStore sPrefStore;
+    public SdkStatsService() {
+    }
 
     /**
      * Send a "ping" to the Google toolbar server, if enough time has
@@ -58,19 +50,8 @@
      * @param app name to report in the ping
      * @param version to report in the ping
      */
-    public static void ping(final String app, final String version) {
-        doPing(app, version, getPreferenceStore());
-    }
-
-    /**
-     * Find out if user has already set permissions for the ping service.
-     * @return true if user has already set the permissions for the ping service. This could've
-     * happened only if the user has already viewed the dialog displayed by
-     * {@link #getUserPermissionForPing(Shell)}.
-     */
-    public static boolean pingPermissionsSet() {
-        PreferenceStore prefs = getPreferenceStore();
-        return prefs != null && prefs.contains(PING_ID);
+    public void ping(String app, String version) {
+        doPing(app, version);
     }
 
     /**
@@ -80,108 +61,60 @@
      * Once the dialog has been shown, it sets a preference internally indicating that the user has
      * viewed this dialog. This setting can be queried using {@link #pingPermissionsSet()}.
      */
-    public static void getUserPermissionForPing(Shell parent) {
-        PreferenceStore prefs = getPreferenceStore();
-        getUserPermissionForPing(prefs, parent);
-
-        // First time: make up a new ID.  TODO: Use something more random?
-        prefs.setValue(PING_ID, new Random().nextLong());
-        try {
-            prefs.save();
-        } catch (IOException e) {
-            /* ignore exceptions while saving preferences */
+    public void checkUserPermissionForPing(Shell parent) {
+        if (!mStore.hasPingId()) {
+            askUserPermissionForPing(parent);
+            mStore.generateNewPingId();
         }
     }
 
     /**
-     * Returns the DDMS {@link PreferenceStore}.
+     * Prompt the user for whether they want to opt out of reporting, and save the user
+     * input in preferences.
      */
-    public static synchronized PreferenceStore getPreferenceStore() {
-        if (sPrefStore == null) {
-            // get the location of the preferences
-            String homeDir = null;
-            try {
-                homeDir = AndroidLocation.getFolder();
-            } catch (AndroidLocationException e1) {
-                // pass, we'll do a dummy store since homeDir is null
+    private void askUserPermissionForPing(final Shell parent) {
+        final Display display = parent.getDisplay();
+        display.syncExec(new Runnable() {
+            public void run() {
+                SdkStatsPermissionDialog dialog = new SdkStatsPermissionDialog(parent);
+                dialog.open();
+                mStore.setPingOptIn(dialog.getPingUserPreference());
             }
-
-            if (homeDir != null) {
-                String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$
-
-                // also look for an old pref file in the previous location
-                String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$
-                    + File.separator + ".ddmsrc"; //$NON-NLS-1$
-                File oldPrefFile = new File(oldPrefPath);
-                if (oldPrefFile.isFile()) {
-                    try {
-                        PreferenceStore oldStore = new PreferenceStore(oldPrefPath);
-                        oldStore.load();
-
-                        oldStore.save(new FileOutputStream(rcFileName), "");
-                        oldPrefFile.delete();
-
-                        PreferenceStore newStore = new PreferenceStore(rcFileName);
-                        newStore.load();
-                        sPrefStore = newStore;
-                    } catch (IOException e) {
-                        // create a new empty store.
-                        sPrefStore = new PreferenceStore(rcFileName);
-                    }
-                } else {
-                    sPrefStore = new PreferenceStore(rcFileName);
-
-                    try {
-                        sPrefStore.load();
-                    } catch (IOException e) {
-                        System.err.println("Error Loading Preferences");
-                    }
-                }
-            } else {
-                sPrefStore = new PreferenceStore();
-            }
-        }
-
-        return sPrefStore;
+        });
     }
 
+    // -------
+
     /**
      * Pings the usage stats server, as long as the prefs contain the opt-in boolean
      *
      * @param app name to report in the ping
      * @param version to report in the ping
-     * @param prefs the preference store where the opt-in value and ping times are store
      */
-    private static void doPing(final String app, String version, PreferenceStore prefs) {
+    private void doPing(final String app, String version) {
         // Validate the application and version input.
         final String normalVersion = normalizeVersion(app, version);
 
         // If the user has not opted in, do nothing and quietly return.
-        if (!prefs.getBoolean(PING_OPT_IN)) {
+        if (!mStore.isPingOptIn()) {
             // user opted out.
             return;
         }
 
         // If the last ping *for this app* was too recent, do nothing.
-        String timePref = PING_TIME + "." + app;  //$NON-NLS-1$
         long now = System.currentTimeMillis();
-        long then = prefs.getLong(timePref);
+        long then = mStore.getPingTime(app);
         if (now - then < PING_INTERVAL_MSEC) {
             // too soon after a ping.
             return;
         }
 
         // Record the time of the attempt, whether or not it succeeds.
-        prefs.setValue(timePref, now);
-        try {
-            prefs.save();
-        }
-        catch (IOException ioe) {
-        }
+        mStore.setPingTime(app, now);
 
         // Send the ping itself in the background (don't block if the
         // network is down or slow or confused).
-        final long id = prefs.getLong(PING_ID);
+        final long id = mStore.getPingId();
         new Thread() {
             @Override
             public void run() {
@@ -269,21 +202,6 @@
     }
 
     /**
-     * Prompt the user for whether they want to opt out of reporting, and save the user
-     * input in preferences.
-     */
-    private static void getUserPermissionForPing(final PreferenceStore prefs, final Shell parent) {
-        final Display display = parent.getDisplay();
-        display.syncExec(new Runnable() {
-            public void run() {
-                SdkStatsPermissionDialog dialog = new SdkStatsPermissionDialog(parent);
-                dialog.open();
-                prefs.setValue(PING_OPT_IN, dialog.getPingUserPreference());
-            }
-        });
-    }
-
-    /**
      * Validate the supplied application version, and normalize the version.
      * @param app to report
      * @param version supplied by caller