Merge "Prevent scaling beyond zoom overview scale."
diff --git a/api/current.xml b/api/current.xml
index f02ec65..708a852 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -20161,17 +20161,6 @@
  visibility="public"
 >
 </method>
-<method name="getGetter"
- return="java.lang.reflect.Method"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getPropertyName"
  return="java.lang.String"
  abstract="false"
@@ -20183,17 +20172,6 @@
  visibility="public"
 >
 </method>
-<method name="getSetter"
- return="java.lang.reflect.Method"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="ofFloat"
  return="android.animation.PropertyValuesHolder"
  abstract="false"
@@ -20282,19 +20260,6 @@
 <parameter name="values" type="float...">
 </parameter>
 </method>
-<method name="setGetter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="getter" type="java.lang.reflect.Method">
-</parameter>
-</method>
 <method name="setIntValues"
  return="void"
  abstract="false"
@@ -20347,19 +20312,6 @@
 <parameter name="propertyName" type="java.lang.String">
 </parameter>
 </method>
-<method name="setSetter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="setter" type="java.lang.reflect.Method">
-</parameter>
-</method>
 </class>
 <class name="RGBEvaluator"
  extends="java.lang.Object"
@@ -250407,7 +250359,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 22e04a7..5fe3644 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -36,22 +36,36 @@
      * this call, because all animation events are posted to a central timing loop so that animation
      * times are all synchronized on a single timing pulse on the UI thread. So the animation will
      * start the next time that event handler processes events.
+     *
+     * <p>The animation started by calling this method will be run on the thread that called
+     * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+     * this is not the case). Also, if the animation will animate
+     * properties of objects in the view hierarchy, then the calling thread should be the UI
+     * thread for that view hierarchy.</p>
+     *
      */
     public void start() {
     }
 
     /**
      * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
-     * stop in its tracks, sending an {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
-     * its listeners, followed by an {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
+     * stop in its tracks, sending an
+     * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
+     * its listeners, followed by an
+     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
+     *
+     * <p>This method must be called on the thread that is running the animation.</p>
      */
     public void cancel() {
     }
 
     /**
      * Ends the animation. This causes the animation to assign the end value of the property being
-     * animated, then calling the {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
+     * animated, then calling the
+     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
      * its listeners.
+     *
+     * <p>This method must be called on the thread that is running the animation.</p>
      */
     public void end() {
     }
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 154e084..d77dbdc 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -96,6 +96,9 @@
     // The amount of time in ms to delay starting the animation after start() is called
     private long mStartDelay = 0;
 
+    // Animator used for a nonzero startDelay
+    private ValueAnimator mDelayAnim = null;
+
 
     // How long the child animations should last in ms. The default value is negative, which
     // simply means that there is no duration set on the AnimatorSet. When a real duration is
@@ -276,6 +279,19 @@
                 listener.onAnimationCancel(this);
             }
         }
+        if (mDelayAnim != null && mDelayAnim.isRunning()) {
+            // If we're currently in the startDelay period, just cancel that animator and
+            // send out the end event to all listeners
+            mDelayAnim.cancel();
+            if (mListeners != null) {
+                ArrayList<AnimatorListener> tmpListeners =
+                        (ArrayList<AnimatorListener>) mListeners.clone();
+                for (AnimatorListener listener : tmpListeners) {
+                    listener.onAnimationEnd(this);
+                }
+            }
+            return;
+        }
         if (mSortedNodes.size() > 0) {
             for (Node node : mSortedNodes) {
                 node.animation.cancel();
@@ -302,6 +318,9 @@
                 node.animation.addListener(mSetListener);
             }
         }
+        if (mDelayAnim != null) {
+            mDelayAnim.cancel();
+        }
         if (mSortedNodes.size() > 0) {
             for (Node node : mSortedNodes) {
                 node.animation.end();
@@ -411,12 +430,25 @@
         // contains the animation nodes in the correct order.
         sortNodes();
 
+        int numSortedNodes = mSortedNodes.size();
+        for (int i = 0; i < numSortedNodes; ++i) {
+            Node node = mSortedNodes.get(i);
+            // First, clear out the old listeners
+            ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
+            if (oldListeners != null && oldListeners.size() > 0) {
+                for (AnimatorListener listener : oldListeners) {
+                    if (listener instanceof DependencyListener) {
+                        node.animation.removeListener(listener);
+                    }
+                }
+            }
+        }
+
         // nodesToStart holds the list of nodes to be started immediately. We don't want to
         // start the animations in the loop directly because we first need to set up
         // dependencies on all of the nodes. For example, we don't want to start an animation
         // when some other animation also wants to start when the first animation begins.
         final ArrayList<Node> nodesToStart = new ArrayList<Node>();
-        int numSortedNodes = mSortedNodes.size();
         for (int i = 0; i < numSortedNodes; ++i) {
             Node node = mSortedNodes.get(i);
             if (mSetListener == null) {
@@ -443,19 +475,25 @@
             }
         } else {
             // TODO: Need to cancel out of the delay appropriately
-            ValueAnimator delayAnim = ValueAnimator.ofFloat(0f, 1f);
-            delayAnim.setDuration(mStartDelay);
-            delayAnim.addListener(new AnimatorListenerAdapter() {
+            mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
+            mDelayAnim.setDuration(mStartDelay);
+            mDelayAnim.addListener(new AnimatorListenerAdapter() {
+                boolean canceled = false;
+                public void onAnimationCancel(Animator anim) {
+                    canceled = true;
+                }
                 public void onAnimationEnd(Animator anim) {
-                    int numNodes = nodesToStart.size();
-                    for (int i = 0; i < numNodes; ++i) {
-                        Node node = nodesToStart.get(i);
-                        node.animation.start();
-                        mPlayingSet.add(node.animation);
+                    if (!canceled) {
+                        int numNodes = nodesToStart.size();
+                        for (int i = 0; i < numNodes; ++i) {
+                            Node node = nodesToStart.get(i);
+                            node.animation.start();
+                            mPlayingSet.add(node.animation);
+                        }
                     }
                 }
             });
-            delayAnim.start();
+            mDelayAnim.start();
         }
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 7c2e70d..7f11871 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 
 /**
  * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
@@ -31,6 +32,7 @@
  *
  */
 public final class ObjectAnimator extends ValueAnimator {
+    private static final boolean DBG = false;
 
     // The target object on which the property exists, set in the constructor
     private Object mTarget;
@@ -265,6 +267,21 @@
         }
     }
 
+    @Override
+    public void start() {
+        if (DBG) {
+            Log.d("ObjectAnimator", "Anim target, duration" + mTarget + ", " + getDuration());
+            for (int i = 0; i < mValues.length; ++i) {
+                PropertyValuesHolder pvh = mValues[i];
+                ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
+                Log.d("ObjectAnimator", "   Values[" + i + "]: " +
+                    pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
+                    keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
+            }
+        }
+        super.start();
+    }
+
     /**
      * This function is called immediately before processing the first animation
      * frame of an animation. If there is a nonzero <code>startDelay</code>, the
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 97aa5a1..286c03b 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -544,67 +544,6 @@
     }
 
     /**
-     * Sets the <code>Method</code> that is called with the animated values calculated
-     * during the animation. Setting the setter method is an alternative to supplying a
-     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
-     * approach is more direct, and is especially useful when a function must be called that does
-     * not correspond to the convention of <code>setName()</code>. For example, if a function
-     * called <code>offset()</code> is to be called with the animated values, there is no way
-     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
-     * name, so a setter method should be supplied instead.
-     *
-     * <p>Note that the setter function must take the same parameter type as the
-     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
-     * the setter function will fail.</p>
-     *
-     * @param setter The setter method that should be called with the animated values.
-     */
-    public void setSetter(Method setter) {
-        mSetter = setter;
-    }
-
-    /**
-     * Gets the <code>Method</code> that is called with the animated values calculated
-     * during the animation.
-     */
-    public Method getSetter() {
-        return mSetter;
-    }
-
-    /**
-     * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
-     * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a
-     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
-     * approach is more direct, and is especially useful when a function must be called that does
-     * not correspond to the convention of <code>setName()</code>. For example, if a function
-     * called <code>offset()</code> is to be called to get an initial value, there is no way
-     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
-     * name, so a getter method should be supplied instead.
-     *
-     * <p>Note that the getter method is only called whether supplied here or derived
-     * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are
-     * null. If both of those values are non-null, then there is no need to get one of the
-     * values and the getter is not called.
-     *
-     * <p>Note that the getter function must return the same parameter type as the
-     * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are
-     * non-null), otherwise the call to the getter function will fail.</p>
-     *
-     * @param getter The getter method that should be called to get initial animation values.
-     */
-    public void setGetter(Method getter) {
-        mGetter = getter;
-    }
-
-    /**
-     * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
-     * <code>valueTo</code> properties.
-     */
-    public Method getGetter() {
-        return mGetter;
-    }
-
-    /**
      * Sets the name of the property that will be animated. This name is used to derive
      * a setter function that will be called to set animated values.
      * For example, a property name of <code>foo</code> will result
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 8e541c2..1542c49 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -62,9 +62,7 @@
      */
     private static final int STOPPED    = 0; // Not yet playing
     private static final int RUNNING    = 1; // Playing normally
-    private static final int CANCELED   = 2; // cancel() called - need to end it
-    private static final int ENDED      = 3; // end() called - need to end it
-    private static final int SEEKED     = 4; // Seeked to some time value
+    private static final int SEEKED     = 2; // Seeked to some time value
 
     /**
      * Internal variables
@@ -178,7 +176,7 @@
      * Flag that represents the current state of the animation. Used to figure out when to start
      * an animation (if state == STOPPED). Also used to end an animation that
      * has been cancel()'d or end()'d since the last animation frame. Possible values are
-     * STOPPED, RUNNING, ENDED, CANCELED.
+     * STOPPED, RUNNING, SEEKED.
      */
     private int mPlayingState = STOPPED;
 
@@ -581,8 +579,7 @@
                         for (int i = 0; i < count; ++i) {
                             ValueAnimator anim = pendingCopy.get(i);
                             // If the animation has a startDelay, place it on the delayed list
-                            if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
-                                    anim.mPlayingState == CANCELED) {
+                            if (anim.mStartDelay == 0) {
                                 anim.startAnimation();
                             } else {
                                 delayedAnims.add(anim);
@@ -619,14 +616,28 @@
                     // Now process all active animations. The return value from animationFrame()
                     // tells the handler whether it should now be ended
                     int numAnims = animations.size();
-                    for (int i = 0; i < numAnims; ++i) {
+                    int i = 0;
+                    while (i < numAnims) {
                         ValueAnimator anim = animations.get(i);
                         if (anim.animationFrame(currentTime)) {
                             endingAnims.add(anim);
                         }
+                        if (animations.size() == numAnims) {
+                            ++i;
+                        } else {
+                            // An animation might be canceled or ended by client code
+                            // during the animation frame. Check to see if this happened by
+                            // seeing whether the current index is the same as it was before
+                            // calling animationFrame(). Another approach would be to copy
+                            // animations to a temporary list and process that list instead,
+                            // but that entails garbage and processing overhead that would
+                            // be nice to avoid.
+                            --numAnims;
+                            endingAnims.remove(anim);
+                        }
                     }
                     if (endingAnims.size() > 0) {
-                        for (int i = 0; i < endingAnims.size(); ++i) {
+                        for (i = 0; i < endingAnims.size(); ++i) {
                             endingAnims.get(i).endAnimation();
                         }
                         endingAnims.clear();
@@ -918,9 +929,7 @@
         // to run
         if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
                 sDelayedAnims.get().contains(this)) {
-            // Just set the CANCELED flag - this causes the animation to end the next time a frame
-            // is processed.
-            mPlayingState = CANCELED;
+            endAnimation();
         }
     }
 
@@ -929,23 +938,21 @@
         if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
             // Special case if the animation has not yet started; get it ready for ending
             mStartedDelay = false;
-            sPendingAnimations.get().add(this);
-            AnimationHandler animationHandler = sAnimationHandler.get();
-            if (animationHandler == null) {
-                animationHandler = new AnimationHandler();
-                sAnimationHandler.set(animationHandler);
-            }
-            animationHandler.sendEmptyMessage(ANIMATION_START);
+            startAnimation();
         }
-        // Just set the ENDED flag - this causes the animation to end the next time a frame
-        // is processed.
-        mPlayingState = ENDED;
+        // The final value set on the target varies, depending on whether the animation
+        // was supposed to repeat an odd number of times
+        if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+            animateValue(0f);
+        } else {
+            animateValue(1f);
+        }
+        endAnimation();
     }
 
     @Override
     public boolean isRunning() {
-        // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
-        return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
+        return (mPlayingState == RUNNING);
     }
 
     /**
@@ -973,6 +980,8 @@
      */
     private void endAnimation() {
         sAnimations.get().remove(this);
+        sPendingAnimations.get().remove(this);
+        sDelayedAnims.get().remove(this);
         mPlayingState = STOPPED;
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
@@ -1014,10 +1023,6 @@
      * should be added to the set of active animations.
      */
     private boolean delayedAnimationFrame(long currentTime) {
-        if (mPlayingState == CANCELED || mPlayingState == ENDED) {
-            // end the delay, process an animation frame to actually cancel it
-            return true;
-        }
         if (!mStartedDelay) {
             mStartedDelay = true;
             mDelayStartTime = currentTime;
@@ -1088,19 +1093,6 @@
             }
             animateValue(fraction);
             break;
-        case ENDED:
-            // The final value set on the target varies, depending on whether the animation
-            // was supposed to repeat an odd number of times
-            if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
-                animateValue(0f);
-            } else {
-                animateValue(1f);
-            }
-            // Fall through to set done flag
-        case CANCELED:
-            done = true;
-            mPlayingState = STOPPED;
-            break;
         }
 
         return done;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d2de382..f1842c6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -532,10 +532,6 @@
         throw new RuntimeException("Not supported in system context");
     }
 
-    static File makeBackupFile(File prefsFile) {
-        return new File(prefsFile.getPath() + ".bak");
-    }
-
     public File getSharedPrefsFile(String name) {
         return makeFilename(getPreferencesDir(), name + ".xml");
     }
@@ -543,54 +539,19 @@
     @Override
     public SharedPreferences getSharedPreferences(String name, int mode) {
         SharedPreferencesImpl sp;
-        File prefsFile;
-        boolean needInitialLoad = false;
         synchronized (sSharedPrefs) {
             sp = sSharedPrefs.get(name);
-            if (sp != null && !sp.hasFileChangedUnexpectedly()) {
-                return sp;
-            }
-            prefsFile = getSharedPrefsFile(name);
             if (sp == null) {
-                sp = new SharedPreferencesImpl(prefsFile, mode, null);
+                File prefsFile = getSharedPrefsFile(name);
+                sp = new SharedPreferencesImpl(prefsFile, mode);
                 sSharedPrefs.put(name, sp);
-                needInitialLoad = true;
-            }
-        }
-
-        synchronized (sp) {
-            if (needInitialLoad && sp.isLoaded()) {
-                // lost the race to load; another thread handled it
                 return sp;
             }
-            File backup = makeBackupFile(prefsFile);
-            if (backup.exists()) {
-                prefsFile.delete();
-                backup.renameTo(prefsFile);
-            }
-
-            // Debugging
-            if (prefsFile.exists() && !prefsFile.canRead()) {
-                Log.w(TAG, "Attempt to read preferences file " + prefsFile + " without permission");
-            }
-
-            Map map = null;
-            FileStatus stat = new FileStatus();
-            if (FileUtils.getFileStatus(prefsFile.getPath(), stat) && prefsFile.canRead()) {
-                try {
-                    FileInputStream str = new FileInputStream(prefsFile);
-                    map = XmlUtils.readMapXml(str);
-                    str.close();
-                } catch (XmlPullParserException e) {
-                    Log.w(TAG, "getSharedPreferences", e);
-                } catch (FileNotFoundException e) {
-                    Log.w(TAG, "getSharedPreferences", e);
-                } catch (IOException e) {
-                    Log.w(TAG, "getSharedPreferences", e);
-                }
-            }
-            sp.replace(map, stat);
         }
+        // If somebody else (some other process) changed the prefs
+        // file behind our back, we reload it.  This has been the
+        // historical (if undocumented) behavior.
+        sp.startReloadIfChangedUnexpectedly();
         return sp;
     }
 
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index a807d3b..8aee65c 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -25,9 +25,12 @@
 import com.google.android.collect.Maps;
 import com.android.internal.util.XmlUtils;
 
+import dalvik.system.BlockGuard;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -61,32 +64,88 @@
 
     private final Object mWritingToDiskLock = new Object();
     private static final Object mContent = new Object();
-    private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
+    private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
+            new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
 
-    SharedPreferencesImpl(
-        File file, int mode, Map initialContents) {
+    SharedPreferencesImpl(File file, int mode) {
         mFile = file;
-        mBackupFile = ContextImpl.makeBackupFile(file);
+        mBackupFile = makeBackupFile(file);
         mMode = mode;
-        mLoaded = initialContents != null;
-        mMap = initialContents != null ? initialContents : new HashMap<String, Object>();
-        FileStatus stat = new FileStatus();
-        if (FileUtils.getFileStatus(file.getPath(), stat)) {
-            mStatTimestamp = stat.mtime;
-        }
-        mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
+        mLoaded = false;
+        mMap = null;
+        startLoadFromDisk();
     }
 
-    // Has this SharedPreferences ever had values assigned to it?
-    boolean isLoaded() {
+    private void startLoadFromDisk() {
         synchronized (this) {
-            return mLoaded;
+            mLoaded = false;
+        }
+        new Thread("SharedPreferencesImpl-load") {
+            public void run() {
+                synchronized (SharedPreferencesImpl.this) {
+                    loadFromDiskLocked();
+                }
+            }
+        }.start();
+    }
+
+    private void loadFromDiskLocked() {
+        if (mLoaded) {
+            return;
+        }
+        if (mBackupFile.exists()) {
+            mFile.delete();
+            mBackupFile.renameTo(mFile);
+        }
+
+        // Debugging
+        if (mFile.exists() && !mFile.canRead()) {
+            Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
+        }
+
+        Map map = null;
+        FileStatus stat = new FileStatus();
+        if (FileUtils.getFileStatus(mFile.getPath(), stat) && mFile.canRead()) {
+            try {
+                FileInputStream str = new FileInputStream(mFile);
+                map = XmlUtils.readMapXml(str);
+                str.close();
+            } catch (XmlPullParserException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            } catch (IOException e) {
+                Log.w(TAG, "getSharedPreferences", e);
+            }
+        }
+        mLoaded = true;
+        if (map != null) {
+            mMap = map;
+            mStatTimestamp = stat.mtime;
+            mStatSize = stat.size;
+        } else {
+            mMap = new HashMap<String, Object>();
+        }
+        notifyAll();
+    }
+
+    private static File makeBackupFile(File prefsFile) {
+        return new File(prefsFile.getPath() + ".bak");
+    }
+
+    void startReloadIfChangedUnexpectedly() {
+        synchronized (this) {
+            // TODO: wait for any pending writes to disk?
+            if (!hasFileChangedUnexpectedly()) {
+                return;
+            }
+            startLoadFromDisk();
         }
     }
 
     // Has the file changed out from under us?  i.e. writes that
     // we didn't instigate.
-    public boolean hasFileChangedUnexpectedly() {
+    private boolean hasFileChangedUnexpectedly() {
         synchronized (this) {
             if (mDiskWritesInFlight > 0) {
                 // If we know we caused it, it's not unexpected.
@@ -103,19 +162,6 @@
         }
     }
 
-    /*package*/ void replace(Map newContents, FileStatus stat) {
-        synchronized (this) {
-            mLoaded = true;
-            if (newContents != null) {
-                mMap = newContents;
-            }
-            if (stat != null) {
-                mStatTimestamp = stat.mtime;
-                mStatSize = stat.size;
-            }
-        }
-    }
-
     public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
         synchronized(this) {
             mListeners.put(listener, mContent);
@@ -128,8 +174,24 @@
         }
     }
 
+    private void awaitLoadedLocked() {
+        if (!mLoaded) {
+            // Raise an explicit StrictMode onReadFromDisk for this
+            // thread, since the real read will be in a different
+            // thread and otherwise ignored by StrictMode.
+            BlockGuard.getThreadPolicy().onReadFromDisk();
+        }
+        while (!mLoaded) {
+            try {
+                wait();
+            } catch (InterruptedException unused) {
+            }
+        }
+    }
+
     public Map<String, ?> getAll() {
-        synchronized(this) {
+        synchronized (this) {
+            awaitLoadedLocked();
             //noinspection unchecked
             return new HashMap<String, Object>(mMap);
         }
@@ -137,6 +199,7 @@
 
     public String getString(String key, String defValue) {
         synchronized (this) {
+            awaitLoadedLocked();
             String v = (String)mMap.get(key);
             return v != null ? v : defValue;
         }
@@ -144,6 +207,7 @@
 
     public Set<String> getStringSet(String key, Set<String> defValues) {
         synchronized (this) {
+            awaitLoadedLocked();
             Set<String> v = (Set<String>) mMap.get(key);
             return v != null ? v : defValues;
         }
@@ -151,24 +215,28 @@
 
     public int getInt(String key, int defValue) {
         synchronized (this) {
+            awaitLoadedLocked();
             Integer v = (Integer)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public long getLong(String key, long defValue) {
         synchronized (this) {
+            awaitLoadedLocked();
             Long v = (Long)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public float getFloat(String key, float defValue) {
         synchronized (this) {
+            awaitLoadedLocked();
             Float v = (Float)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public boolean getBoolean(String key, boolean defValue) {
         synchronized (this) {
+            awaitLoadedLocked();
             Boolean v = (Boolean)mMap.get(key);
             return v != null ? v : defValue;
         }
@@ -176,11 +244,23 @@
 
     public boolean contains(String key) {
         synchronized (this) {
+            awaitLoadedLocked();
             return mMap.containsKey(key);
         }
     }
 
     public Editor edit() {
+        // TODO: remove the need to call awaitLoadedLocked() when
+        // requesting an editor.  will require some work on the
+        // Editor, but then we should be able to do:
+        //
+        //      context.getSharedPreferences(..).edit().putString(..).apply()
+        //
+        // ... all without blocking.
+        synchronized (this) {
+            awaitLoadedLocked();
+        }
+
         return new EditorImpl();
     }
 
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index a759865..daa1c09 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -189,6 +189,9 @@
         return -1;
     }
 
+    public void setDataEnable(boolean enabled) {
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Dummy data state: none, dummy!");
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 4df8db5..b3a354f 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -20,14 +20,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ServiceManager;
+
+import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo;
 import android.net.LinkProperties;
@@ -67,6 +74,10 @@
     // the other is also disconnected before we reset sockets
     private boolean mIsDefaultOrHipri = false;
 
+    private Handler mHandler;
+    private AsyncChannel mDataConnectionTrackerAc;
+    private Messenger mMessenger;
+
     /**
      * Create a new MobileDataStateTracker
      * @param netType the ConnectivityManager network type
@@ -107,14 +118,54 @@
         mTarget = target;
         mContext = context;
 
+        HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+        handlerThread.start();
+        mHandler = new MdstHandler(handlerThread.getLooper(), this);
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
+        filter.addAction(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
         mMobileDataState = Phone.DataState.DISCONNECTED;
     }
 
+    static class MdstHandler extends Handler {
+        private MobileDataStateTracker mMdst;
+
+        MdstHandler(Looper looper, MobileDataStateTracker mdst) {
+            super(looper);
+            mMdst = mdst;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        if (DBG) {
+                            mMdst.log("MdstHandler connected");
+                        }
+                        mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
+                    } else {
+                        if (DBG) {
+                            mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
+                        }
+                    }
+                    break;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                    mMdst.log("Disconnected from DataStateTracker");
+                    mMdst.mDataConnectionTrackerAc = null;
+                    break;
+                default: {
+                    mMdst.log("Ignorning unknown message=" + msg);
+                    break;
+                }
+            }
+        }
+    }
+
     /**
      * Return the IP addresses of the DNS servers available for the mobile data
      * network interface.
@@ -179,7 +230,7 @@
                         false));
 
                 if (DBG) {
-                    log(mApnType + " Received state=" + state + ", old=" + mMobileDataState +
+                    log("Received state=" + state + ", old=" + mMobileDataState +
                         ", reason=" + (reason == null ? "(unspecified)" : reason));
                 }
                 if (mMobileDataState != state) {
@@ -265,10 +316,16 @@
                 String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
                 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                 if (DBG) {
-                    log(mApnType + "Received " + intent.getAction() +
+                    log("Received " + intent.getAction() +
                                 " broadcast" + reason == null ? "" : "(" + reason + ")");
                 }
                 setDetailedState(DetailedState.FAILED, reason, apnName);
+            } else if (intent.getAction().
+                    equals(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER)) {
+                if (DBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER");
+                mMessenger = intent.getParcelableExtra(DataConnectionTracker.EXTRA_MESSENGER);
+                AsyncChannel ac = new AsyncChannel();
+                ac.connect(mContext, MobileDataStateTracker.this.mHandler, mMessenger);
             } else {
                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
             }
@@ -488,6 +545,20 @@
         return -1;
     }
 
+    /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled) {
+        try {
+            log("setDataEnable: E enabled=" + enabled);
+            mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
+                    enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
+            log("setDataEnable: X enabled=" + enabled);
+        } catch (Exception e) {
+            log("setDataEnable: X mAc was null" + e);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Mobile data state: ");
@@ -543,7 +614,7 @@
             case ConnectivityManager.TYPE_MOBILE_HIPRI:
                 return Phone.APN_TYPE_HIPRI;
             default:
-                loge("Error mapping networkType " + netType + " to apnType.");
+                sloge("Error mapping networkType " + netType + " to apnType.");
                 return null;
         }
     }
@@ -562,11 +633,15 @@
         return new LinkCapabilities(mLinkCapabilities);
     }
 
-    static private void log(String s) {
-        Slog.d(TAG, s);
+    private void log(String s) {
+        Slog.d(TAG, mApnType + ": " + s);
     }
 
-    static private void loge(String s) {
+    private void loge(String s) {
+        Slog.e(TAG, mApnType + ": " + s);
+    }
+
+    static private void sloge(String s) {
         Slog.e(TAG, s);
     }
 }
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 8afdcee..e378506 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -169,6 +169,11 @@
     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
 
     /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled);
+
+    /**
      * -------------------------------------------------------------
      * Storage API used by ConnectivityService for saving
      * Network specific information.
@@ -204,5 +209,4 @@
      * Indicate tear down requested from connectivity
      */
     public void setTeardownRequested(boolean isRequested);
-
 }
diff --git a/core/java/com/android/internal/widget/DrawableHolder.java b/core/java/com/android/internal/widget/DrawableHolder.java
index a528aeb..947e0f3 100644
--- a/core/java/com/android/internal/widget/DrawableHolder.java
+++ b/core/java/com/android/internal/widget/DrawableHolder.java
@@ -87,15 +87,12 @@
      * @param property
      */
     public void removeAnimationFor(String property) {
-        ArrayList<ObjectAnimator> removalList = new ArrayList<ObjectAnimator>();
-        for (ObjectAnimator currentAnim : mAnimators) {
+        ArrayList<ObjectAnimator> removalList = (ArrayList<ObjectAnimator>)mAnimators.clone();
+        for (ObjectAnimator currentAnim : removalList) {
             if (property.equals(currentAnim.getPropertyName())) {
                 currentAnim.cancel();
-                removalList.add(currentAnim);
             }
         }
-        if (DBG) Log.v(TAG, "Remove list size: " + removalList.size());
-        mAnimators.removeAll(removalList);
     }
 
     /**
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 758f9f3..9d11d87 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -387,7 +387,6 @@
          * the number of different network types is not going
          * to change very often.
          */
-        boolean noMobileData = !getMobileDataEnabled();
         for (int netType : mPriorityList) {
             switch (mNetAttributes[netType].mRadio) {
             case ConnectivityManager.TYPE_WIFI:
@@ -407,10 +406,6 @@
                 mNetTrackers[netType] = new MobileDataStateTracker(netType,
                         mNetAttributes[netType].mName);
                 mNetTrackers[netType].startMonitoring(context, mHandler);
-                if (noMobileData) {
-                    if (DBG) log("tearing down Mobile networks due to setting");
-                    mNetTrackers[netType].teardown();
-                }
                 break;
             case ConnectivityManager.TYPE_DUMMY:
                 mNetTrackers[netType] = new DummyDataStateTracker(netType,
@@ -691,10 +686,6 @@
         // TODO - move this into the MobileDataStateTracker
         int usedNetworkType = networkType;
         if(networkType == ConnectivityManager.TYPE_MOBILE) {
-            if (!getMobileDataEnabled()) {
-                if (DBG) log("requested special network with data disabled - rejected");
-                return Phone.APN_TYPE_NOT_AVAILABLE;
-            }
             if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
                 usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
             } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
@@ -985,6 +976,9 @@
      * @see ConnectivityManager#getMobileDataEnabled()
      */
     public boolean getMobileDataEnabled() {
+        // TODO: This detail should probably be in DataConnectionTracker's
+        //       which is where we store the value and maybe make this
+        //       asynchronous.
         enforceAccessPermission();
         boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.MOBILE_DATA, 1) == 1;
@@ -1004,42 +998,14 @@
     }
 
     private void handleSetMobileData(boolean enabled) {
-        if (getMobileDataEnabled() == enabled) return;
-
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
-
-        if (enabled) {
-            if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) {
-                    log("starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
-                }
-                mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
+        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
+            if (DBG) {
+                Slog.d(TAG, mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
             }
-        } else {
-            for (NetworkStateTracker nt : mNetTrackers) {
-                if (nt == null) continue;
-                int netType = nt.getNetworkInfo().getType();
-                if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) {
-                    if (DBG) log("tearing down " + nt);
-                    nt.teardown();
-                }
-            }
+            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
         }
     }
 
-    private int getNumConnectedNetworks() {
-        int numConnectedNets = 0;
-
-        for (NetworkStateTracker nt : mNetTrackers) {
-            if (nt != null && nt.getNetworkInfo().isConnected() &&
-                    !nt.isTeardownRequested()) {
-                ++numConnectedNets;
-            }
-        }
-        return numConnectedNets;
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -1159,16 +1125,9 @@
 
             int newType = -1;
             int newPriority = -1;
-            boolean noMobileData = !getMobileDataEnabled();
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
                 if (checkType == prevNetType) continue;
                 if (mNetAttributes[checkType] == null) continue;
-                if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
-                        noMobileData) {
-                    loge("not failing over to mobile type " + checkType +
-                            " because Mobile Data Disabled");
-                    continue;
-                }
                 if (mNetAttributes[checkType].isDefault()) {
                     /* TODO - if we have multiple nets we could use
                      * we may want to put more thought into which we choose
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 069e1b8..737342f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -30,6 +30,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.ServiceManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -81,6 +82,10 @@
         DORMANT
     }
 
+    public static String ACTION_DATA_CONNECTION_TRACKER_MESSENGER =
+        "com.android.internal.telephony";
+    public static String EXTRA_MESSENGER = "EXTRA_MESSENGER";
+
     /***** Event Codes *****/
     protected static final int EVENT_DATA_SETUP_COMPLETE = 1;
     protected static final int EVENT_RADIO_AVAILABLE = 3;
@@ -113,6 +118,8 @@
     protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37;
     protected static final int EVENT_RESET_DONE = 38;
 
+    public static final int CMD_SET_DATA_ENABLE = 39;
+
     /***** Constants *****/
 
     protected static final int APN_INVALID_ID = -1;
@@ -123,13 +130,18 @@
     protected static final int APN_HIPRI_ID = 4;
     protected static final int APN_NUM_TYPES = 5;
 
-    protected static final int DISABLED = 0;
-    protected static final int ENABLED = 1;
+    public static final int DISABLED = 0;
+    public static final int ENABLED = 1;
 
     // responds to the setInternalDataEnabled call - used internally to turn off data
     // for example during emergency calls
     protected boolean mInternalDataEnabled = true;
 
+    // responds to public (user) API to enable/disable data use
+    // independent of mInternalDataEnabled and requests for APN access
+    // persisted
+    protected boolean mDataEnabled = true;
+
     protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
 
     protected int enabledCount = 0;
@@ -289,6 +301,9 @@
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
 
+        mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
+                Settings.Secure.MOBILE_DATA, 1) == 1;
+
         // TODO: Why is this registering the phone as the receiver of the intent
         //       and not its own handler?
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
@@ -296,16 +311,8 @@
         // This preference tells us 1) initial condition for "dataEnabled",
         // and 2) whether the RIL will setup the baseband to auto-PS attach.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        boolean dataEnabledSetting = true;
-        try {
-            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
-                    getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
-        } catch (Exception e) {
-            // nothing to do - use the old behavior and leave data on
-        }
         dataEnabled[APN_DEFAULT_ID] =
-                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false) &&
-                dataEnabledSetting;
+                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
         if (dataEnabled[APN_DEFAULT_ID]) {
             enabledCount++;
         }
@@ -316,6 +323,12 @@
         mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
     }
 
+    protected void broadcastMessenger() {
+        Intent intent = new Intent(ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
+        intent.putExtra(EXTRA_MESSENGER, new Messenger(this));
+        mPhone.getContext().sendBroadcast(intent);
+    }
+
     public Activity getActivity() {
         return mActivity;
     }
@@ -490,14 +503,20 @@
                 onCleanUpConnection(tearDown, (String) msg.obj);
                 break;
 
-            case EVENT_SET_INTERNAL_DATA_ENABLE:
+            case EVENT_SET_INTERNAL_DATA_ENABLE: {
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
                 onSetInternalDataEnabled(enabled);
                 break;
-
+            }
             case EVENT_RESET_DONE:
                 onResetDone((AsyncResult) msg.obj);
                 break;
+            case CMD_SET_DATA_ENABLE: {
+                log("CMD_SET_DATA_ENABLE msg=" + msg);
+                boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                onSetDataEnabled(enabled);
+                break;
+            }
 
             default:
                 Log.e("DATA", "Unidentified event = " + msg.what);
@@ -512,7 +531,7 @@
      *         {@code true} otherwise.
      */
     public synchronized boolean getAnyDataEnabled() {
-        return (mInternalDataEnabled && (enabledCount != 0));
+        return (mInternalDataEnabled && mDataEnabled && (enabledCount != 0));
     }
 
     protected abstract void startNetStatPoll();
@@ -828,11 +847,6 @@
      * Prevent mobile data connections from being established, or once again
      * allow mobile data connections. If the state toggles, then either tear
      * down or set up data, as appropriate to match the new state.
-     * <p>
-     * This operation only affects the default APN, and if the same APN is
-     * currently being used for MMS traffic, the teardown will not happen even
-     * when {@code enable} is {@code false}.
-     * </p>
      *
      * @param enable indicates whether to enable ({@code true}) or disable (
      *            {@code false}) data
@@ -849,15 +863,41 @@
     }
 
     protected void onSetInternalDataEnabled(boolean enable) {
+        boolean prevEnabled = getAnyDataEnabled();
         if (mInternalDataEnabled != enable) {
             synchronized (this) {
                 mInternalDataEnabled = enable;
             }
-            if (enable) {
-                mRetryMgr.resetRetryCount();
-                onTrySetupData(Phone.REASON_DATA_ENABLED);
-            } else {
-                onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+            if (prevEnabled != getAnyDataEnabled()) {
+                if (!prevEnabled) {
+                    mRetryMgr.resetRetryCount();
+                    onTrySetupData(Phone.REASON_DATA_ENABLED);
+                } else {
+                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                }
+            }
+        }
+    }
+
+    public synchronized boolean getDataEnabled() {
+        return mDataEnabled;
+    }
+
+    protected void onSetDataEnabled(boolean enable) {
+        boolean prevEnabled = getAnyDataEnabled();
+        if (mDataEnabled != enable) {
+            synchronized (this) {
+                mDataEnabled = enable;
+            }
+            Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+                    Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
+            if (prevEnabled != getAnyDataEnabled()) {
+                if (!prevEnabled) {
+                    mRetryMgr.resetRetryCount();
+                    onTrySetupData(Phone.REASON_DATA_ENABLED);
+                } else {
+                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                }
             }
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index b005cd3..60df7df 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -113,6 +113,7 @@
         mDataConnectionTracker = this;
 
         createAllDataConnectionList();
+        broadcastMessenger();
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 4713c24..cd0d9e3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -160,6 +160,7 @@
 
         /** Create the default connection */
         createDataConnection(Phone.APN_TYPE_DEFAULT);
+        broadcastMessenger();
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index d0231be..2aff7ec 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -170,6 +170,13 @@
     }
 
     /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled) {
+        android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    }
+
+    /**
      * Check if private DNS route is set for the network
      */
     public boolean isPrivateDnsRouteSet() {