Merge "Camera2: remove CameraDevice#getProperties" into klp-dev
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 97cb815..61e071b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4679,8 +4679,6 @@
             assumeLayout();
         }
 
-        boolean changed = false;
-
         if (mMovement != null) {
             /* This code also provides auto-scrolling when a cursor is moved using a
              * CursorController (insertion point or selection limits).
@@ -4703,10 +4701,10 @@
             }
 
             if (curs >= 0) {
-                changed = bringPointIntoView(curs);
+                bringPointIntoView(curs);
             }
         } else {
-            changed = bringTextIntoView();
+            bringTextIntoView();
         }
 
         // This has to be checked here since:
@@ -4727,7 +4725,7 @@
         getViewTreeObserver().removeOnPreDrawListener(this);
         mPreDrawRegistered = false;
 
-        return !changed;
+        return true;
     }
 
     @Override
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 009b729..fbdf318 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -33,6 +33,7 @@
 import android.media.SubtitleTrack.RenderingWidget;
 import android.media.WebVttRenderer;
 import android.net.Uri;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
@@ -879,4 +880,10 @@
 
         invalidate();
     }
+
+    /** @hide */
+    @Override
+    public Looper getSubtitleLooper() {
+        return Looper.getMainLooper();
+    }
 }
diff --git a/docs/html/guide/topics/ui/controls/checkbox.jd b/docs/html/guide/topics/ui/controls/checkbox.jd
index 99140b5..2a64e38 100644
--- a/docs/html/guide/topics/ui/controls/checkbox.jd
+++ b/docs/html/guide/topics/ui/controls/checkbox.jd
@@ -94,7 +94,7 @@
 android.view.View} that was clicked)</li>
 </ul>
 
-<p class="note"><strong>Tip:</strong> If you need to change the radio button state
+<p class="note"><strong>Tip:</strong> If you need to change the checkbox state
 yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
 use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
 android.widget.CompoundButton#toggle()} method.</p>
\ No newline at end of file
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 09767bf..d934c4b 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -593,7 +593,7 @@
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Send the negative button event back to the host activity
-                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
+                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
                    }
                });
         return builder.create();
diff --git a/docs/html/training/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd
index d3f68e2..f52865a 100644
--- a/docs/html/training/articles/security-ssl.jd
+++ b/docs/html/training/articles/security-ssl.jd
@@ -250,7 +250,7 @@
 This is similar to an unknown certificate authority, so you can use the
 same approach from the previous section.</p>
 
-<p>You can create yout own {@link javax.net.ssl.TrustManager},
+<p>You can create your own {@link javax.net.ssl.TrustManager},
 this time trusting the server certificate directly. This has all of the
 downsides discussed earlier of tying your app directly to a certificate, but can be done
 securely. However, you should be careful to make sure your self-signed certificate has a
diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd
index 0f667d7..7240eec 100644
--- a/docs/html/training/articles/smp.jd
+++ b/docs/html/training/articles/smp.jd
@@ -1057,7 +1057,7 @@
 fix them.  Before we do that, we need to discuss the use of a basic language
 feature.</p>
 
-<h4 id="volatile">C/C+++ and "volatile"</h4>
+<h4 id="volatile">C/C++ and "volatile"</h4>
 
 <p>When writing single-threaded code, declaring a variable “volatile” can be
 very useful.  The compiler will not omit or reorder accesses to volatile
diff --git a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
index 11a05e1..fb5096d 100644
--- a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
+++ b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
@@ -49,7 +49,8 @@
         (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  
 NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
-boolean isConnected = activeNetwork.isConnectedOrConnecting();</pre>
+boolean isConnected = activeNetwork != null &&
+                      activeNetwork.isConnectedOrConnecting();</pre>
 
 
 <h2 id="DetermineType">Determine the Type of your Internet Connection</h2> 
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index a3c9dac..77ac235 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -576,29 +576,6 @@
           </li>
         </ul>
       </li>
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>training/id-auth/index.html"
-             description=
-             "How to remember the user by account, authenticate the user, acquire user permission
-             for the user's online data, and create custom accounts on the device."
-            >Remembering Users</a>
-        </div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>training/id-auth/identify.html">
-            Remembering Your User
-          </a>
-          </li>
-          <li><a href="<?cs var:toroot ?>training/id-auth/authenticate.html">
-            Authenticating to OAuth2 Services
-          </a>
-          </li>
-          <li><a href="<?cs var:toroot ?>training/id-auth/custom_auth.html">
-            Creating a Custom Account Type
-          </a>
-          </li>
-        </ul>
-      </li>
 
       <li class="nav-section">
         <div class="nav-section-header">
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index deba2cc..def9aa7 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1772,6 +1772,9 @@
             mSelectedSubtitleTrackIndex = -1;
         }
         setOnSubtitleDataListener(null);
+        if (track == null) {
+            return;
+        }
         for (int i = 0; i < mInbandSubtitleTracks.length; i++) {
             if (mInbandSubtitleTracks[i] == track) {
                 Log.v(TAG, "Selecting subtitle track " + i);
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 5127479..06af5de 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -374,7 +374,7 @@
          * Called when a sound has completed loading.
          *
          * @param soundPool SoundPool object from the load() method
-         * @param soundPool the sample ID of the sound loaded.
+         * @param sampleId the sample ID of the sound loaded.
          * @param status the status of the load operation (0 = success)
          */
         public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
index 8090561..13205bc 100644
--- a/media/java/android/media/SubtitleController.java
+++ b/media/java/android/media/SubtitleController.java
@@ -21,6 +21,9 @@
 
 import android.content.Context;
 import android.media.SubtitleTrack.RenderingWidget;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.view.accessibility.CaptioningManager;
 
 /**
@@ -37,6 +40,34 @@
     private SubtitleTrack mSelectedTrack;
     private boolean mShowing;
     private CaptioningManager mCaptioningManager;
+    private Handler mHandler;
+
+    private static final int WHAT_SHOW = 1;
+    private static final int WHAT_HIDE = 2;
+    private static final int WHAT_SELECT_TRACK = 3;
+    private static final int WHAT_SELECT_DEFAULT_TRACK = 4;
+
+    private final Handler.Callback mCallback = new Handler.Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+            case WHAT_SHOW:
+                doShow();
+                return true;
+            case WHAT_HIDE:
+                doHide();
+                return true;
+            case WHAT_SELECT_TRACK:
+                doSelectTrack((SubtitleTrack)msg.obj);
+                return true;
+            case WHAT_SELECT_DEFAULT_TRACK:
+                doSelectDefaultTrack();
+                return true;
+            default:
+                return false;
+            }
+        }
+    };
 
     private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener =
         new CaptioningManager.CaptioningChangeListener() {
@@ -112,7 +143,7 @@
      * in-band data from the {@link MediaPlayer}.  However, this does
      * not change the subtitle visibility.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      *
      * @param track The subtitle track to select.  This must be one of the
      *              tracks in {@link #getTracks}.
@@ -122,9 +153,15 @@
         if (track != null && !mTracks.contains(track)) {
             return false;
         }
+
+        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track));
+        return true;
+    }
+
+    private void doSelectTrack(SubtitleTrack track) {
         mTrackIsExplicit = true;
         if (mSelectedTrack == track) {
-            return true;
+            return;
         }
 
         if (mSelectedTrack != null) {
@@ -145,7 +182,6 @@
         if (mListener != null) {
             mListener.onSubtitleTrackSelected(track);
         }
-        return true;
     }
 
     /**
@@ -170,8 +206,6 @@
      *
      * The default values for these flags are DEFAULT=no, AUTOSELECT=yes
      * and FORCED=no.
-     *
-     * Must be called from the UI thread.
      */
     public SubtitleTrack getDefaultTrack() {
         SubtitleTrack bestTrack = null;
@@ -226,8 +260,12 @@
     private boolean mTrackIsExplicit = false;
     private boolean mVisibilityIsExplicit = false;
 
-    /** @hide - called from UI thread */
+    /** @hide - should be called from anchor thread */
     public void selectDefaultTrack() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK));
+    }
+
+    private void doSelectDefaultTrack() {
         if (mTrackIsExplicit) {
             // If track selection is explicit, but visibility
             // is not, it falls back to the captioning setting
@@ -259,8 +297,9 @@
         }
     }
 
-    /** @hide - called from UI thread */
+    /** @hide - must be called from anchor thread */
     public void reset() {
+        checkAnchorLooper();
         hide();
         selectTrack(null);
         mTracks.clear();
@@ -301,9 +340,13 @@
     /**
      * Show the selected (or default) subtitle track.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      */
     public void show() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_SHOW));
+    }
+
+    private void doShow() {
         mShowing = true;
         mVisibilityIsExplicit = true;
         if (mSelectedTrack != null) {
@@ -314,9 +357,13 @@
     /**
      * Hide the selected (or default) subtitle track.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      */
     public void hide() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_HIDE));
+    }
+
+    private void doHide() {
         mVisibilityIsExplicit = true;
         if (mSelectedTrack != null) {
             mSelectedTrack.hide();
@@ -384,25 +431,53 @@
          * @hide
          */
         public void setSubtitleWidget(RenderingWidget subtitleWidget);
+
+        /**
+         * Anchors provide the looper on which all track visibility changes
+         * (track.show/hide, setSubtitleWidget) will take place.
+         * @hide
+         */
+        public Looper getSubtitleLooper();
     }
 
     private Anchor mAnchor;
 
-    /** @hide - called from UI thread */
+    /**
+     *  @hide - called from anchor's looper (if any, both when unsetting and
+     *  setting)
+     */
     public void setAnchor(Anchor anchor) {
         if (mAnchor == anchor) {
             return;
         }
 
         if (mAnchor != null) {
+            checkAnchorLooper();
             mAnchor.setSubtitleWidget(null);
         }
         mAnchor = anchor;
+        mHandler = null;
         if (mAnchor != null) {
+            mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback);
+            checkAnchorLooper();
             mAnchor.setSubtitleWidget(getRenderingWidget());
         }
     }
 
+    private void checkAnchorLooper() {
+        assert mHandler != null : "Should have a looper already";
+        assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper";
+    }
+
+    private void processOnAnchor(Message m) {
+        assert mHandler != null : "Should have a looper already";
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            mHandler.dispatchMessage(m);
+        } else {
+            mHandler.sendMessage(m);
+        }
+    }
+
     public interface Listener {
         /**
          * Called when a subtitle track has been selected.