Merge "Return display name in SipConnection.getCnapName()." into gingerbread
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index a27ba84..6ff5a40 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -175,13 +175,18 @@
          * camera image needs to be rotated clockwise so it shows correctly on
          * the display in its natural orientation. It should be 0, 90, 180, or 270.
          *
-         * For example, suppose a device has a naturally tall screen, but the camera
-         * sensor is mounted in landscape. If the top side of the camera sensor is
-         * aligned with the right edge of the display in natural orientation, the
-         * value should be 90.
+         * For example, suppose a device has a naturally tall screen. The
+         * back-facing camera sensor is mounted in landscape. You are looking at
+         * the screen. If the top side of the camera sensor is aligned with the
+         * right edge of the screen in natural orientation, the value should be
+         * 90. If the top side of a front-facing camera sensor is aligned with
+         * the right of the screen, the value should be 270.
          *
          * @see #setDisplayOrientation(int)
          * @see #setRotation(int)
+         * @see #setPreviewSize(int, int)
+         * @see #setPictureSize(int, int)
+         * @see #setJpegThumbnailSize(int, int)
          */
         public int orientation;
     };
@@ -771,13 +776,16 @@
     public native final void stopSmoothZoom();
 
     /**
-     * Set the display orientation. This affects the preview frames and the
-     * picture displayed after snapshot. This method is useful for portrait
-     * mode applications.
+     * Set the clockwise rotation of preview display in degrees. This affects
+     * the preview frames and the picture displayed after snapshot. This method
+     * is useful for portrait mode applications. Note that preview display of
+     * front-facing cameras is flipped horizontally, that is, the image is
+     * reflected along the central vertical axis of the camera sensor. So the
+     * users can see themselves as looking into a mirror.
      *
-     * This does not affect the order of byte array passed in
-     * {@link PreviewCallback#onPreviewFrame}. This method is not allowed to
-     * be called during preview.
+     * This does not affect the order of byte array passed in {@link
+     * PreviewCallback#onPreviewFrame}, JPEG pictures, or recorded videos. This
+     * method is not allowed to be called during preview.
      *
      * If you want to make the camera image show in the same orientation as
      * the display, you can use the following code.<p>
@@ -797,13 +805,20 @@
      *         case Surface.ROTATION_270: degrees = 270; break;
      *     }
      *
-     *     int result = (info.orientation - degrees + 360) % 360;
+     *     int result;
+     *     if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+     *         result = (info.orientation + degrees) % 360;
+     *         result = (360 - result) % 360;  // compensate the mirror
+     *     } else {  // back-facing
+     *         result = (info.orientation - degrees + 360) % 360;
+     *     }
      *     camera.setDisplayOrientation(result);
      * }
      * </pre>
      * @param degrees the angle that the picture will be rotated clockwise.
      *                Valid values are 0, 90, 180, and 270. The starting
      *                position is 0 (landscape).
+     * @see #setPreviewDisplay(SurfaceHolder)
      */
     public native final void setDisplayOrientation(int degrees);
 
@@ -1749,20 +1764,23 @@
          * the orientation in the EXIF header will be missing or 1 (row #0 is
          * top and column #0 is left side).
          *
-         * If appplications want to rotate the picture to match the
-         * orientation of what users see, apps should use {@link
+         * If applications want to rotate the picture to match the orientation
+         * of what users see, apps should use {@link
          * android.view.OrientationEventListener} and {@link CameraInfo}.
          * The value from OrientationEventListener is relative to the natural
-         * orientation of the device. CameraInfo.mOrientation is the angle
-         * between camera orientation and natural device orientation. The sum
-         * of the two is the angle for rotation.
+         * orientation of the device. CameraInfo.orientation is the angle
+         * between camera orientation and natural device orientation. The sum or
+         * of the two is the rotation angle for back-facing camera. The
+         * difference of the two is the rotation angle for front-facing camera.
+         * Note that the JPEG pictures of front-facing cameras are not mirrored
+         * as in preview display.
          *
          * For example, suppose the natural orientation of the device is
          * portrait. The device is rotated 270 degrees clockwise, so the device
-         * orientation is 270. Suppose the camera sensor is mounted in landscape
-         * and the top side of the camera sensor is aligned with the right edge
-         * of the display in natural orientation. So the camera orientation is
-         * 90. The rotation should be set to 0 (270 + 90).
+         * orientation is 270. Suppose a back-facing camera sensor is mounted in
+         * landscape and the top side of the camera sensor is aligned with the
+         * right edge of the display in natural orientation. So the camera
+         * orientation is 90. The rotation should be set to 0 (270 + 90).
          *
          * The reference code is as follows.
          *
@@ -1772,7 +1790,13 @@
          *            new android.hardware.Camera.CameraInfo();
          *     android.hardware.Camera.getCameraInfo(cameraId, info);
          *     orientation = (orientation + 45) / 90 * 90;
-         *     mParameters.setRotation((orientation + info.mOrientation) % 360);
+         *     int rotation = 0;
+         *     if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
+         *         rotation = (info.orientation - orientation + 360) % 360;
+         *     } else {  // back-facing camera
+         *         rotation = (info.orientation + orientation) % 360;
+         *     }
+         *     mParameters.setRotation(rotation);
          * }
          *
          * @param rotation The rotation angle in degrees relative to the
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 58ed986..be818da 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -50,7 +50,7 @@
         }
         try {
             mService.vibrate(milliseconds, mToken);
-        } catch (Exception e) {
+        } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
     }
@@ -80,7 +80,7 @@
         if (repeat < pattern.length) {
             try {
                 mService.vibratePattern(pattern, repeat, mToken);
-            } catch (Exception e) {
+            } catch (RemoteException e) {
                 Log.w(TAG, "Failed to vibrate.", e);
             }
         } else {
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index f7afdb9..4f192b3 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.text.Editable;
 import android.text.InputFilter;
+import android.text.Layout;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextPaint;
@@ -292,8 +293,9 @@
      * Ensure that the underlying textfield is lined up with the WebTextView.
      */
     private void lineUpScroll() {
-        if (mWebView != null) {
-            float maxScrollX = Touch.getMaxScrollX(this, getLayout(), mScrollY);
+        Layout layout = getLayout();
+        if (mWebView != null && layout != null) {
+            float maxScrollX = Touch.getMaxScrollX(this, layout, mScrollY);
             if (DebugFlags.WEB_TEXT_VIEW) {
                 Log.v(LOGTAG, "onTouchEvent x=" + mScrollX + " y="
                         + mScrollY + " maxX=" + maxScrollX);
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d38eef3..fee3455 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -551,6 +551,10 @@
                                 mEdgeGlowLeft.onRelease();
                             }
                         }
+                        if (mEdgeGlowLeft != null
+                                && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
+                            invalidate();
+                        }
                     }
                 }
                 break;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1daf2ab..2ad67ba 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -546,6 +546,10 @@
                                 mEdgeGlowTop.onRelease();
                             }
                         }
+                        if (mEdgeGlowTop != null
+                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
+                            invalidate();
+                        }
                     }
                 }
                 break;
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index 1fcd654..b7255bb 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -360,7 +360,13 @@
         } else if (SHUTDOWN_VIBRATE_MS > 0) {
             // vibrate before shutting down
             Vibrator vibrator = new Vibrator();
-            vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+            try {
+                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+            } catch (Exception e) {
+                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
+                Log.w(TAG, "Failed to vibrate during shutdown.", e);
+            }
+
             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
             try {
                 Thread.sleep(SHUTDOWN_VIBRATE_MS);
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index f789301..fc1e866 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -1094,7 +1094,9 @@
      * @param msg that couldn't be handled.
      */
     protected void unhandledMessage(Message msg) {
-        Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
+        if (false) {
+            Log.e(TAG, mName + " - unhandledMessage: msg.what=" + msg.what);
+        }
     }
 
     /**
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index 495fd80..063a10f 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -42,7 +42,7 @@
 
 <p>Android {@sdkPlatformVersion} is a {@sdkPlatformMajorMinor} platform release including user
 features, developer features, API changes, and bug
-fixes. For  information on developer features and API changes, see the 
+fixes. For  information on developer features and API changes, see the
 <a href="#api">Framework API</a> section.</p>
 
 <p>For developers, the Android {@sdkPlatformVersion} platform is available as a
@@ -62,7 +62,7 @@
 
 <h2 id="features">Platform Highlights</h2>
 
-<p>For a list of new user features and platform highlights, see the <a 
+<p>For a list of new user features and platform highlights, see the <a
 href="http://developer.android.com/sdk/android-2.2-highlights.html">Android
 2.2 Platform Highlights</a> document.</p>
 
@@ -316,8 +316,8 @@
 
 <ul>
   <li> New <code>android:backupAgent</code> attribute of the
-<code>&lt;application&gt;</code> element. Specifies the component name of the 
-BackupAgent subclass provided by the application to handle backup/restore 
+<code>&lt;application&gt;</code> element. Specifies the component name of the
+BackupAgent subclass provided by the application to handle backup/restore
 operations, if any.</li>
   <li> New <code>android:restoreAnyVersion</code> attribute of the
 <code>&lt;application&gt;</code> element. Boolean value that indicates whether
@@ -436,6 +436,11 @@
 <p>Localized UI strings match the locales that are accessible
 through Settings.</p>
 
+<p class="note"><strong>Note:</strong> Android supports more locales than are listed above. However,
+the entire collection of locale strings cannot fit on a single system image, so the above list is
+only what's included in the system image for the SDK. All of Android's supported locales are
+available in the <a href="http://source.android.com/">Android Open Source Project</a>.</p>
+
 <h2 id="skins">Emulator Skins</h2>
 
 <p>The downloadable platform includes a set of emulator skins that you can use
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp
index 7ebde78..1f62ac5 100644
--- a/libs/utils/StreamingZipInflater.cpp
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_NDEBUG 1
 #define LOG_TAG "szipinf"
 #include <utils/Log.h>
 
@@ -157,7 +158,7 @@
             */
             int result = Z_OK;
             if (mStreamNeedsInit) {
-                LOGI("Initializing zlib to inflate");
+                LOGD("Initializing zlib to inflate");
                 result = inflateInit2(&mInflateState, -MAX_WBITS);
                 mStreamNeedsInit = false;
             }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 57bea8c..064a00c 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1059,6 +1059,32 @@
     return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
 }
 
+void AwesomePlayer::finishSeekIfNecessary(int64_t videoTimeUs) {
+    if (!mSeeking) {
+        return;
+    }
+
+    if (mAudioPlayer != NULL) {
+        LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
+
+        // If we don't have a video time, seek audio to the originally
+        // requested seek time instead.
+
+        mAudioPlayer->seekTo(videoTimeUs < 0 ? mSeekTimeUs : videoTimeUs);
+        mAudioPlayer->resume();
+        mWatchForAudioSeekComplete = true;
+        mWatchForAudioEOS = true;
+    } else if (!mSeekNotificationSent) {
+        // If we're playing video only, report seek complete now,
+        // otherwise audio player will notify us later.
+        notifyListener_l(MEDIA_SEEK_COMPLETE);
+    }
+
+    mFlags |= FIRST_FRAME;
+    mSeeking = false;
+    mSeekNotificationSent = false;
+}
+
 void AwesomePlayer::onVideoEvent() {
     Mutex::Autolock autoLock(mLock);
     if (!mVideoEventPending) {
@@ -1120,6 +1146,14 @@
                     continue;
                 }
 
+                // So video playback is complete, but we may still have
+                // a seek request pending that needs to be applied
+                // to the audio track.
+                if (mSeeking) {
+                    LOGV("video stream ended while seeking!");
+                }
+                finishSeekIfNecessary(-1);
+
                 mFlags |= VIDEO_AT_EOS;
                 postStreamDoneEvent_l(err);
                 return;
@@ -1146,24 +1180,7 @@
         mVideoTimeUs = timeUs;
     }
 
-    if (mSeeking) {
-        if (mAudioPlayer != NULL) {
-            LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
-
-            mAudioPlayer->seekTo(timeUs);
-            mAudioPlayer->resume();
-            mWatchForAudioSeekComplete = true;
-            mWatchForAudioEOS = true;
-        } else if (!mSeekNotificationSent) {
-            // If we're playing video only, report seek complete now,
-            // otherwise audio player will notify us later.
-            notifyListener_l(MEDIA_SEEK_COMPLETE);
-        }
-
-        mFlags |= FIRST_FRAME;
-        mSeeking = false;
-        mSeekNotificationSent = false;
-    }
+    finishSeekIfNecessary(timeUs);
 
     TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 46a0c65..3020a09 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -258,6 +258,8 @@
 
     bool getBitrate(int64_t *bitrate);
 
+    void finishSeekIfNecessary(int64_t videoTimeUs);
+
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
 };
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 163b2dbc..3fb322a 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -840,7 +840,7 @@
 #define VERSION_MAJOR 1
 #define VERSION_MINOR 2
 static char const * const gVendorString     = "Google Inc.";
-static char const * const gVersionString    = "1.2 Android Driver 1.1.0";
+static char const * const gVersionString    = "1.2 Android Driver 1.2.0";
 static char const * const gClientApiString  = "OpenGL ES";
 static char const * const gExtensionsString =
         "EGL_KHR_image_base "
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index a0f720a..8b4136a 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -33,7 +33,7 @@
 // ----------------------------------------------------------------------------
 
 static char const * const gVendorString     = "Android";
-static char const * const gRendererString   = "Android PixelFlinger 1.3";
+static char const * const gRendererString   = "Android PixelFlinger 1.4";
 static char const * const gVersionString    = "OpenGL ES-CM 1.0";
 static char const * const gExtensionsString =
     "GL_OES_byte_coordinates "              // OK
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 0309430..9ef6e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -710,7 +710,6 @@
         NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra(
                 ConnectivityManager.EXTRA_NETWORK_INFO));
         int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0);
-        Slog.d(TAG, "got CONNECTIVITY_ACTION - info=" + info + ", status = " + connectionStatus);
 
         int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index b1c6ad8..de30ccd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -392,8 +392,6 @@
     }
 
     public void updateNotification(IBinder key, StatusBarNotification notification) {
-        Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
-
         NotificationData oldList;
         int oldIndex = mOngoing.findEntry(key);
         if (oldIndex >= 0) {
@@ -628,27 +626,27 @@
 
         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
-                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");
                 animateCollapse();
             }
         }
         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
                 if (mTicking) {
                     mTicker.halt();
                 } else {
                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
                 }
             } else {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
                 if (!mExpandedVisible) {
                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
                 }
             }
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
                 mTicker.halt();
             }
         }
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 88203c3..9d10ce5 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -93,8 +93,8 @@
  */
 public class KeyguardViewMediator implements KeyguardViewCallback,
         KeyguardUpdateMonitor.SimStateCallback {
-    private final static boolean DEBUG = false && Config.LOGD;
-    private final static boolean DBG_WAKE = DEBUG || true;
+    private final static boolean DEBUG = false;
+    private final static boolean DBG_WAKE = false;
 
     private final static String TAG = "KeyguardViewMediator";
 
@@ -605,7 +605,7 @@
      * @see #onWakeKeyWhenKeyguardShowingTq(int)
      */
     private void wakeWhenReadyLocked(int keyCode) {
-        if (DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
+        if (true || DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
 
         /**
          * acquire the handoff lock that will keep the cpu running.  this will
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 50b3abe..a7e02a5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -84,7 +84,7 @@
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
@@ -664,11 +664,11 @@
                 // backup.
                 RandomAccessFile in = null;
                 try {
-                    Slog.i(TAG, "Found stale backup journal, scheduling:");
+                    Slog.i(TAG, "Found stale backup journal, scheduling");
                     in = new RandomAccessFile(f, "r");
                     while (true) {
                         String packageName = in.readUTF();
-                        Slog.i(TAG, "    + " + packageName);
+                        Slog.i(TAG, "  " + packageName);
                         dataChangedImpl(packageName);
                     }
                 } catch (EOFException e) {
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 0a5b72b..e8c1613 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -684,9 +684,6 @@
     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
             String tag, int id, Notification notification, int[] idOut)
     {
-        Slog.d(TAG, "enqueueNotificationWithTag: calling uid=" + callingUid 
-                + ", pid=" + callingPid);
-        
         checkIncomingCall(pkg);
 
         // Limit the number of notifications that any given package except the android
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index cc7a027..d324c2b 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -2500,7 +2500,9 @@
             return;
         }
 
-        Log.d(TAG, "Scanning app dir " + dir);
+        if (false) {
+            Log.d(TAG, "Scanning app dir " + dir);
+        }
 
         int i;
         for (i=0; i<files.length; i++) {
@@ -2866,10 +2868,8 @@
                 TAG, "Scanning package " + pkg.packageName);
         if (mPackages.containsKey(pkg.packageName)
                 || mSharedLibraries.containsKey(pkg.packageName)) {
-            Slog.w(TAG, "*************************************************");
             Slog.w(TAG, "Application package " + pkg.packageName
                     + " already installed.  Skipping duplicate.");
-            Slog.w(TAG, "*************************************************");
             mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
             return null;
         }
@@ -3286,7 +3286,9 @@
              *        only for non-system apps and system app upgrades.
              */
             if (pkg.applicationInfo.nativeLibraryDir != null) {
-                final File sharedLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+                final File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
+                final String dataPathString = dataPath.getPath();
+
                 if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
                     /*
                      * Upgrading from a previous version of the OS sometimes
@@ -3295,15 +3297,24 @@
                      * Recent changes in the JNI library search path
                      * necessitates we remove those to match previous behavior.
                      */
-                    if (NativeLibraryHelper.removeNativeBinariesFromDirLI(sharedLibraryDir)) {
+                    if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
                         Log.i(TAG, "removed obsolete native libraries for system package " + path);
                     }
-                } else if (!isExternal(pkg)) {
-                    Log.i(TAG, path + " changed; unpacking");
-                    mInstaller.unlinkNativeLibraryDirectory(dataPath.getPath());
-                    NativeLibraryHelper.copyNativeBinariesLI(scanFile, sharedLibraryDir);
+                } else if (nativeLibraryDir.getParent().equals(dataPathString)) {
+                    /*
+                     * If this is an internal application or our
+                     * nativeLibraryPath points to our data directory, unpack
+                     * the libraries. The native library path pointing to the
+                     * data directory for an application in an ASEC container
+                     * can happen for older apps that existed before an OTA to
+                     * Gingerbread.
+                     */
+                    Slog.i(TAG, "Unpacking native libraries for " + path);
+                    mInstaller.unlinkNativeLibraryDirectory(dataPathString);
+                    NativeLibraryHelper.copyNativeBinariesLI(scanFile, nativeLibraryDir);
                 } else {
-                    mInstaller.linkNativeLibraryDirectory(dataPath.getPath(),
+                    Slog.i(TAG, "Linking native library dir for " + path);
+                    mInstaller.linkNativeLibraryDirectory(dataPathString,
                             pkg.applicationInfo.nativeLibraryDir);
                 }
             }
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 4177432..e5dfb18 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -54,7 +54,7 @@
 public class StatusBarManagerService extends IStatusBarService.Stub
 {
     static final String TAG = "StatusBarManagerService";
-    static final boolean SPEW = true;
+    static final boolean SPEW = false;
 
     final Context mContext;
     Handler mHandler = new Handler();
@@ -154,7 +154,6 @@
         synchronized (mDisableRecords) {
             manageDisableListLocked(what, token, pkg);
             final int net = gatherDisableActionsLocked();
-            Slog.d(TAG, "disable... net=0x" + Integer.toHexString(net));
             if (net != mDisabled) {
                 mDisabled = net;
                 mHandler.post(new Runnable() {
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 7e23422..33702793 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -254,6 +254,7 @@
         if (!checkNotifyPermission("notifyServiceState()")){
             return;
         }
+        Slog.i(TAG, "notifyServiceState: " + state);
         synchronized (mRecords) {
             mServiceState = state;
             for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -295,6 +296,7 @@
         if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
             return;
         }
+        Slog.i(TAG, "notifyMessageWaitingChanged: " + mwi);
         synchronized (mRecords) {
             mMessageWaiting = mwi;
             for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -314,6 +316,7 @@
         if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
             return;
         }
+        Slog.i(TAG, "notifyCallForwardingChanged: " + cfi);
         synchronized (mRecords) {
             mCallForwarding = cfi;
             for (int i = mRecords.size() - 1; i >= 0; i--) {
@@ -354,6 +357,9 @@
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
+        Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
+                + isDataConnectivityPossible + " reason=" + reason
+                + " interfaceName=" + interfaceName + " networkType=" + networkType);
         synchronized (mRecords) {
             mDataConnectionState = state;
             mDataConnectionPossible = isDataConnectivityPossible;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index a63b3d8..c1e9965 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1656,7 +1656,9 @@
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                Slog.d(TAG, "ACTION_SCREEN_ON");
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_SCREEN_ON");
+                }
                 mAlarmManager.cancel(mIdleIntent);
                 mDeviceIdle = false;
                 mScreenOff = false;
@@ -1671,7 +1673,9 @@
                     sendEnableNetworksMessage();
                 }
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                Slog.d(TAG, "ACTION_SCREEN_OFF");
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_SCREEN_OFF");
+                }
                 mScreenOff = true;
                 mWifiStateTracker.enableRssiPolling(false);
                 /*
@@ -1688,21 +1692,28 @@
                         // as long as we would if connected (below)
                         // TODO - fix the race conditions and switch back to the immediate turn-off
                         long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
-                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+                        if (DBG) {
+                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms");
+                        }
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                         //  // do not keep Wifi awake when screen is off if Wifi is not associated
                         //  mDeviceIdle = true;
                         //  updateWifiState();
                     } else {
                         long triggerTime = System.currentTimeMillis() + idleMillis;
-                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                        if (DBG) {
+                            Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis
+                                    + "ms");
+                        }
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                     }
                 }
                 /* we can return now -- there's nothing to do until we get the idle intent back */
                 return;
             } else if (action.equals(ACTION_DEVICE_IDLE)) {
-                Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+                if (DBG) {
+                    Slog.d(TAG, "got ACTION_DEVICE_IDLE");
+                }
                 mDeviceIdle = true;
                 reportStartWorkSource();
             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
@@ -1714,11 +1725,15 @@
                  * the already-set timer.
                  */
                 int pluggedType = intent.getIntExtra("plugged", 0);
-                Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
+                if (DBG) {
+                    Slog.d(TAG, "ACTION_BATTERY_CHANGED pluggedType: " + pluggedType);
+                }
                 if (mScreenOff && shouldWifiStayAwake(stayAwakeConditions, mPluggedType) &&
                         !shouldWifiStayAwake(stayAwakeConditions, pluggedType)) {
                     long triggerTime = System.currentTimeMillis() + idleMillis;
-                    Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                    if (DBG) {
+                        Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
+                    }
                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                     mPluggedType = pluggedType;
                     return;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 1bc5e4b..a73a4ce 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -120,7 +120,6 @@
     static final long USB_DISCONNECT_DELAY = 1000;
 
     public Tethering(Context context, Looper looper) {
-        Log.d(TAG, "Tethering starting");
         mContext = context;
         mLooper = looper;
 
@@ -457,7 +456,6 @@
                 mUsbMassStorageOff = true;
                 updateUsbStatus();
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
                 mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
                 mBooted = true;
diff --git a/services/java/com/android/server/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
index bc96980..813d255 100644
--- a/services/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -44,6 +44,7 @@
 public class GpsXtraDownloader {
 
     private static final String TAG = "GpsXtraDownloader";
+    static final boolean DEBUG = false;
     
     private Context mContext;
     private String[] mXtraServers;
@@ -107,7 +108,7 @@
 
     protected static byte[] doDownload(String url, boolean isProxySet, 
             String proxyHost, int proxyPort) {
-        if (Config.LOGD) Log.d(TAG, "Downloading XTRA data from " + url);
+        if (DEBUG) Log.d(TAG, "Downloading XTRA data from " + url);
 
         AndroidHttpClient client = null;
         try {
@@ -130,7 +131,7 @@
             HttpResponse response = client.execute(req);
             StatusLine status = response.getStatusLine();
             if (status.getStatusCode() != 200) { // HTTP 200 is success.
-                if (Config.LOGD) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+                if (DEBUG) Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
                 return null;
             }
 
@@ -159,7 +160,7 @@
             }
             return body;
         } catch (Exception e) {
-            if (Config.LOGD) Log.d(TAG, "error " + e);
+            if (DEBUG) Log.d(TAG, "error " + e);
         } finally {
             if (client != null) {
                 client.close();
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 46ef118..9561c6e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -37,7 +37,7 @@
  */
 
 public class CallerInfoAsyncQuery {
-    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+    private static final boolean DBG = false;
     private static final String LOG_TAG = "CallerInfoAsyncQuery";
 
     private static final int EVENT_NEW_QUERY = 1;
@@ -128,13 +128,13 @@
                     // However, if there is any code that this Handler calls (such as in
                     // super.handleMessage) that DOES place unexpected messages on the
                     // queue, then we need pass these messages on.
-                    if (DBG) log("Unexpected command (CookieWrapper is null): " + msg.what +
+                    if (DBG) Log.d(LOG_TAG, "Unexpected command (CookieWrapper is null): " + msg.what +
                             " ignored by CallerInfoWorkerHandler, passing onto parent.");
 
                     super.handleMessage(msg);
                 } else {
 
-                    if (DBG) log("Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
+                    if (DBG) Log.d(LOG_TAG, "Processing event: " + cw.event + " token (arg1): " + msg.arg1 +
                         " command: " + msg.what + " query URI: " + sanitizeUriToString(args.uri));
 
                     switch (cw.event) {
@@ -190,7 +190,7 @@
          */
         @Override
         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            if (DBG) log("##### onQueryComplete() #####   query complete for token: " + token);
+            if (DBG) Log.d(LOG_TAG, "##### onQueryComplete() #####   query complete for token: " + token);
 
             //get the cookie and notify the listener.
             CookieWrapper cw = (CookieWrapper) cookie;
@@ -199,7 +199,7 @@
                 // from within this code.
                 // However, if there is any code that calls this method, we should
                 // check the parameters to make sure they're viable.
-                if (DBG) log("Cookie is null, ignoring onQueryComplete() request.");
+                if (DBG) Log.d(LOG_TAG, "Cookie is null, ignoring onQueryComplete() request.");
                 return;
             }
 
@@ -228,7 +228,7 @@
                     mCallerInfo = new CallerInfo().markAsVoiceMail();
                 } else {
                     mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor);
-                    if (DBG) log("==> Got mCallerInfo: " + mCallerInfo);
+                    if (DBG) Log.d(LOG_TAG, "==> Got mCallerInfo: " + mCallerInfo);
 
                     // Use the number entered by the user for display.
                     if (!TextUtils.isEmpty(cw.number)) {
@@ -236,7 +236,7 @@
                     }
                 }
 
-                if (DBG) log("constructing CallerInfo object for token: " + token);
+                if (DBG) Log.d(LOG_TAG, "constructing CallerInfo object for token: " + token);
 
                 //notify that we can clean up the queue after this.
                 CookieWrapper endMarker = new CookieWrapper();
@@ -246,7 +246,7 @@
 
             //notify the listener that the query is complete.
             if (cw.listener != null) {
-                if (DBG) log("notifying listener: " + cw.listener.getClass().toString() +
+                if (DBG) Log.d(LOG_TAG, "notifying listener: " + cw.listener.getClass().toString() +
                              " for token: " + token + mCallerInfo);
                 cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
             }
@@ -269,7 +269,7 @@
         CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
         c.allocate(context, contactRef);
 
-        if (DBG) log("starting query for URI: " + contactRef + " handler: " + c.toString());
+        if (DBG) Log.d(LOG_TAG, "starting query for URI: " + contactRef + " handler: " + c.toString());
 
         //create cookieWrapper, start query
         CookieWrapper cw = new CookieWrapper();
@@ -296,9 +296,9 @@
     public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
             OnQueryCompleteListener listener, Object cookie) {
         if (DBG) {
-            log("##### CallerInfoAsyncQuery startQuery()... #####");
-            log("- number: " + /*number*/ "xxxxxxx");
-            log("- cookie: " + cookie);
+            Log.d(LOG_TAG, "##### CallerInfoAsyncQuery startQuery()... #####");
+            Log.d(LOG_TAG, "- number: " + /*number*/ "xxxxxxx");
+            Log.d(LOG_TAG, "- cookie: " + cookie);
         }
 
         // Construct the URI object and query params, and start the query.
@@ -309,7 +309,7 @@
 
         if (PhoneNumberUtils.isUriNumber(number)) {
             // "number" is really a SIP address.
-            if (DBG) log("  - Treating number as a SIP address: " + /*number*/ "xxxxxxx");
+            if (DBG) Log.d(LOG_TAG, "  - Treating number as a SIP address: " + /*number*/ "xxxxxxx");
 
             // We look up SIP addresses directly in the Data table:
             contactRef = Data.CONTENT_URI;
@@ -341,11 +341,11 @@
         }
 
         if (DBG) {
-            log("==> contactRef: " + sanitizeUriToString(contactRef));
-            log("==> selection: " + selection);
+            Log.d(LOG_TAG, "==> contactRef: " + sanitizeUriToString(contactRef));
+            Log.d(LOG_TAG, "==> selection: " + selection);
             if (selectionArgs != null) {
                 for (int i = 0; i < selectionArgs.length; i++) {
-                    log("==> selectionArgs[" + i + "]: " + selectionArgs[i]);
+                    Log.d(LOG_TAG, "==> selectionArgs[" + i + "]: " + selectionArgs[i]);
                 }
             }
         }
@@ -383,7 +383,7 @@
      */
     public void addQueryListener(int token, OnQueryCompleteListener listener, Object cookie) {
 
-        if (DBG) log("adding listener to query: " + sanitizeUriToString(mHandler.mQueryUri) +
+        if (DBG) Log.d(LOG_TAG, "adding listener to query: " + sanitizeUriToString(mHandler.mQueryUri) +
                 " handler: " + mHandler.toString());
 
         //create cookieWrapper, add query request to end of queue.
@@ -431,11 +431,4 @@
             return "";
         }
     }
-
-    /**
-     * static logging method
-     */
-    private static void log(String msg) {
-        Log.d(LOG_TAG, msg);
-    }
 }
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index 2514262..13e6f14 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -365,6 +365,10 @@
             Response response = mMessageFactory.createResponse(
                     Response.BUSY_HERE, request);
 
+            if (inviteTransaction == null) {
+                inviteTransaction = getServerTransaction(event);
+            }
+
             if (inviteTransaction.getState() != TransactionState.COMPLETED) {
                 if (DEBUG) Log.d(TAG, "send BUSY HERE: " + response);
                 inviteTransaction.sendResponse(response);
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 1df08c0..84e0803 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -68,7 +68,7 @@
 public final class SipService extends ISipService.Stub {
     static final String TAG = "SipService";
     static final boolean DEBUGV = false;
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final boolean DEBUG_TIMER = DEBUG && false;
     private static final int EXPIRY_TIME = 3600;
     private static final int SHORT_EXPIRY_TIME = 10;
@@ -79,6 +79,7 @@
     private String mNetworkType;
     private boolean mConnected;
     private WakeupTimer mTimer;
+    private WifiScanProcess mWifiScanProcess;
     private WifiManager.WifiLock mWifiLock;
     private boolean mWifiOnly;
 
@@ -104,7 +105,7 @@
         if (SipManager.isApiSupported(context)) {
             ServiceManager.addService("sip", new SipService(context));
             context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
-            Log.i(TAG, "SIP service started");
+            if (DEBUG) Log.i(TAG, "SIP service started");
         }
     }
 
@@ -222,7 +223,7 @@
         SipSessionGroupExt group = mSipGroups.get(localProfileUri);
         if (group == null) return;
         if (!isCallerCreatorOrRadio(group)) {
-            Log.d(TAG, "only creator or radio can close this profile");
+            Log.w(TAG, "only creator or radio can close this profile");
             return;
         }
 
@@ -244,7 +245,7 @@
         if (isCallerCreatorOrRadio(group)) {
             return group.isOpened();
         } else {
-            Log.i(TAG, "only creator or radio can query on the profile");
+            Log.w(TAG, "only creator or radio can query on the profile");
             return false;
         }
     }
@@ -257,7 +258,7 @@
         if (isCallerCreatorOrRadio(group)) {
             return group.isRegistered();
         } else {
-            Log.i(TAG, "only creator or radio can query on the profile");
+            Log.w(TAG, "only creator or radio can query on the profile");
             return false;
         }
     }
@@ -271,7 +272,7 @@
         if (isCallerCreator(group)) {
             group.setListener(listener);
         } else {
-            Log.i(TAG, "only creator can set listener on the profile");
+            Log.w(TAG, "only creator can set listener on the profile");
         }
     }
 
@@ -285,7 +286,7 @@
             SipSessionGroupExt group = createGroup(localProfile);
             return group.createSession(listener);
         } catch (SipException e) {
-            Log.w(TAG, "createSession()", e);
+            if (DEBUG) Log.d(TAG, "createSession()", e);
             return null;
         }
     }
@@ -303,7 +304,7 @@
             s.connect(InetAddress.getByName("192.168.1.1"), 80);
             return s.getLocalAddress().getHostAddress();
         } catch (IOException e) {
-            Log.w(TAG, "determineLocalIp()", e);
+            if (DEBUG) Log.d(TAG, "determineLocalIp()", e);
             // dont do anything; there should be a connectivity change going
             return null;
         }
@@ -371,6 +372,7 @@
                     mContext.getSystemService(Context.WIFI_SERVICE))
                     .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
             mWifiLock.acquire();
+            if (!mConnected) startWifiScanner();
         }
     }
 
@@ -379,6 +381,20 @@
             if (DEBUG) Log.d(TAG, "~~~~~~~~~~~~~~~~~~~~~ release wifi lock");
             mWifiLock.release();
             mWifiLock = null;
+            stopWifiScanner();
+        }
+    }
+
+    private synchronized void startWifiScanner() {
+        if (mWifiScanProcess == null) {
+            mWifiScanProcess = new WifiScanProcess();
+        }
+        mWifiScanProcess.start();
+    }
+
+    private synchronized void stopWifiScanner() {
+        if (mWifiScanProcess != null) {
+            mWifiScanProcess.stop();
         }
     }
 
@@ -413,8 +429,10 @@
                 for (SipSessionGroupExt group : mSipGroups.values()) {
                     group.onConnectivityChanged(true);
                 }
+                if (isWifi && (mWifiLock != null)) stopWifiScanner();
             } else {
                 mMyWakeLock.reset(); // in case there's a leak
+                if (isWifi && (mWifiLock != null)) startWifiScanner();
             }
         } catch (SipException e) {
             Log.e(TAG, "onConnectivityChanged()", e);
@@ -430,6 +448,21 @@
         }
     }
 
+    private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
+            SipSessionGroup.SipSessionImpl ringingSession) {
+        String callId = ringingSession.getCallId();
+        for (SipSessionGroupExt group : mSipGroups.values()) {
+            if ((group != ringingGroup) && group.containsSession(callId)) {
+                if (DEBUG) Log.d(TAG, "call self: "
+                        + ringingSession.getLocalProfile().getUriString()
+                        + " -> " + group.getLocalProfile().getUriString());
+                return true;
+            }
+        }
+        return false;
+    }
+
+
     private class SipSessionGroupExt extends SipSessionAdapter {
         private SipSessionGroup mSipGroup;
         private PendingIntent mIncomingCallPendingIntent;
@@ -452,6 +485,10 @@
             return mSipGroup.getLocalProfile();
         }
 
+        public boolean containsSession(String callId) {
+            return mSipGroup.containsSession(callId);
+        }
+
         // network connectivity is tricky because network can be disconnected
         // at any instant so need to deal with exceptions carefully even when
         // you think you are connected
@@ -467,7 +504,7 @@
                     return createSipSessionGroup(null, localProfile, password);
                 } else {
                     // recursive
-                    Log.wtf(TAG, "impossible!");
+                    Log.wtf(TAG, "impossible! recursive!");
                     throw new RuntimeException("createSipSessionGroup");
                 }
             }
@@ -551,7 +588,7 @@
                     (SipSessionGroup.SipSessionImpl) s;
             synchronized (SipService.this) {
                 try {
-                    if (!isRegistered()) {
+                    if (!isRegistered() || callingSelf(this, session)) {
                         session.endCall();
                         return;
                     }
@@ -592,6 +629,36 @@
         }
     }
 
+    private class WifiScanProcess implements Runnable {
+        private static final String TAG = "\\WIFI_SCAN/";
+        private static final int INTERVAL = 60;
+        private boolean mRunning = false;
+
+        private WifiManager mWifiManager;
+
+        public void start() {
+            if (mRunning) return;
+            mRunning = true;
+            mTimer.set(INTERVAL * 1000, this);
+        }
+
+        WifiScanProcess() {
+            mWifiManager = (WifiManager)
+                    mContext.getSystemService(Context.WIFI_SERVICE);
+        }
+
+        public void run() {
+            // scan and associate now
+            if (DEBUGV) Log.v(TAG, "just wake up here for wifi scanning...");
+            mWifiManager.startScanActive();
+        }
+
+        public void stop() {
+            mRunning = false;
+            mTimer.cancel(this);
+        }
+    }
+
     // KeepAliveProcess is controlled by AutoRegistrationProcess.
     // All methods will be invoked in sync with SipService.this.
     private class KeepAliveProcess implements Runnable {
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 578bd9b..50ce7dc 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -134,7 +134,8 @@
         SipFactory sipFactory = SipFactory.getInstance();
         Properties properties = new Properties();
         properties.setProperty("javax.sip.STACK_NAME", getStackName());
-        properties.setProperty("javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
+        properties.setProperty(
+                "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
         String outboundProxy = myself.getProxyAddress();
         if (!TextUtils.isEmpty(outboundProxy)) {
             Log.v(TAG, "outboundProxy is " + outboundProxy);
@@ -230,6 +231,10 @@
         }
     }
 
+    synchronized boolean containsSession(String callId) {
+        return mSessionMap.containsKey(callId);
+    }
+
     private synchronized SipSessionImpl getSipSession(EventObject event) {
         String key = SipHelper.getCallId(event);
         SipSessionImpl session = mSessionMap.get(key);
@@ -581,6 +586,7 @@
         }
 
         private void processCommand(EventObject command) throws SipException {
+            if (isLoggable(command)) Log.d(TAG, "process cmd: " + command);
             if (!process(command)) {
                 onError(SipErrorCode.IN_PROGRESS,
                         "cannot initiate a new transaction to execute: "
@@ -1049,6 +1055,13 @@
                 mSipHelper.sendCancel(mClientTransaction);
                 startSessionTimer(CANCEL_CALL_TIMER);
                 return true;
+            } else if (isRequestEvent(Request.INVITE, evt)) {
+                // Call self? Send BUSY HERE so server may redirect the call to
+                // voice mailbox.
+                RequestEvent event = (RequestEvent) evt;
+                mSipHelper.sendInviteBusyHere(event,
+                        event.getServerTransaction());
+                return true;
             }
             return false;
         }
@@ -1350,6 +1363,10 @@
         return DEBUG;
     }
 
+    private static boolean isLoggable(EventObject evt) {
+        return isLoggable(null, evt);
+    }
+
     private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
         if (!isLoggable(s)) return false;
         if (evt == null) return false;