Some sip setting changes and registration fix.

+change add/remove account UI.
+add auto. registration per profile.
+fix the infinite loop for incorrect username/password during registration.

Change-Id: Ic763073c21a8a6cebac77207981f45c2c2b2d5e4
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9d2b092..29d62a2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -17,9 +17,11 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name">Sip Account Settings</string>
     <string name="sip_settings_activity_title">Sip Settings Activity</string>
-    <string name="auto_registration">Auto Registration</string>
+
+    <string name="auto_reg">Auto Registration</string>
     <string name="call_priority">Outgoing Call via SIP</string>
     <string name="add_sip_account">Add SIP account</string>
+    <string name="remove_sip_account">Remove SIP account</string>
     <string name="sip_account_list">SIP accounts</string>
 
     <string name="sip_menu_save">Save</string>
@@ -48,7 +50,11 @@
     <string name="transport_summary">TCP</string>
     <string name="send_keepalive_title">Send keep-alive</string>
     <string name="send_keepalive_summary">Send SIP keep-alive messages</string>
+    <string name="auto_registration_title">Auto. Registration</string>
+    <string name="auto_registration_summary">Register the profile automatically</string>
 
+    <string name="sip_menu_enable_autoregister">Enable auto registration</string>
+    <string name="sip_menu_disable_autoregister">Disable auto registration</string>
     <string name="sip_menu_register">Register the account</string>
     <string name="sip_menu_unregister">Unregister the account</string>
     <string name="sip_menu_edit">Edit the account</string>
@@ -66,4 +72,5 @@
     <string name="port">Port</string>
     <string name="transport">Protocol</string>
     <string name="send_keepalive">SendKeepAlive</string>
+    <string name="auto_registration">AutoRegistration</string>
 </resources>
diff --git a/res/xml/sip_edit.xml b/res/xml/sip_edit.xml
index fd44bb2..18306a9 100644
--- a/res/xml/sip_edit.xml
+++ b/res/xml/sip_edit.xml
@@ -88,4 +88,11 @@
         android:defaultValue="false"
         android:summary="@string/send_keepalive_summary"
         android:dialogTitle="@string/send_keepalive_title"/>
+
+    <CheckBoxPreference
+        android:key="@string/auto_registration"
+        android:title="@string/auto_registration_title"
+        android:defaultValue="true"
+        android:summary="@string/auto_registration_summary"
+        android:dialogTitle="@string/auto_registration_title"/>
 </PreferenceScreen>
diff --git a/res/xml/sip_setting.xml b/res/xml/sip_setting.xml
index 504b90e..20d7269 100644
--- a/res/xml/sip_setting.xml
+++ b/res/xml/sip_setting.xml
@@ -17,17 +17,14 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
         android:title="@string/sip_settings_activity_title">
 
-    <CheckBoxPreference android:key="auto_registration"
-        android:title="@string/auto_registration">
-    </CheckBoxPreference>
-
     <CheckBoxPreference android:key="sip_call_first"
         android:title="@string/call_priority">
     </CheckBoxPreference>
 
-    <PreferenceScreen android:key="add_sip_account"
-        android:title="@string/add_sip_account">
-    </PreferenceScreen>
+    <CheckBoxPreference android:key="auto_reg"
+        android:title="@string/auto_reg"
+        android:summary="@string/auto_registration_summary">
+    </CheckBoxPreference>
 
     <PreferenceCategory android:key="sip_account_list" android:title="@string/sip_account_list">
     </PreferenceCategory>
diff --git a/src/android/net/sip/SipProfile.java b/src/android/net/sip/SipProfile.java
index 91072c7..5410a33 100644
--- a/src/android/net/sip/SipProfile.java
+++ b/src/android/net/sip/SipProfile.java
@@ -47,6 +47,7 @@
     private String mProtocol = ListeningPoint.UDP;
     private String mProfileName;
     private boolean mSendKeepAlive = false;
+    private boolean mAutoRegistration = true;
 
     /** @hide */
     public static final Parcelable.Creator<SipProfile> CREATOR =
@@ -213,6 +214,19 @@
             return this;
         }
 
+
+        /**
+         * Sets the auto. registration flag.
+         *
+         * @param flag true if the profile will be registered automatically,
+         *      false otherwise
+         * @return this builder object
+         */
+        public Builder setAutoRegistration(boolean flag) {
+            mProfile.mAutoRegistration = flag;
+            return this;
+        }
+
         /**
          * Builds and returns the SIP profile object.
          *
@@ -249,6 +263,7 @@
         mProtocol = in.readString();
         mProfileName = in.readString();
         mSendKeepAlive = (in.readInt() == 0) ? false : true;
+        mAutoRegistration = (in.readInt() == 0) ? false : true;
     }
 
     /** @hide */
@@ -260,6 +275,7 @@
         out.writeString(mProtocol);
         out.writeString(mProfileName);
         out.writeInt(mSendKeepAlive ? 1 : 0);
+        out.writeInt(mAutoRegistration ? 1 : 0);
     }
 
     /** @hide */
@@ -375,4 +391,13 @@
     public boolean getSendKeepAlive() {
         return mSendKeepAlive;
     }
+
+    /**
+     * Gets the flag of 'Auto Registration'.
+     *
+     * @return the flag of registering the profile automatically.
+     */
+    public boolean getAutoRegistration() {
+        return mAutoRegistration;
+    }
 }
diff --git a/src/com/android/settings/sip/SipEditor.java b/src/com/android/settings/sip/SipEditor.java
index 7fcd8fe..45168f6 100644
--- a/src/com/android/settings/sip/SipEditor.java
+++ b/src/com/android/settings/sip/SipEditor.java
@@ -35,6 +35,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.Button;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -68,7 +69,8 @@
         ProxyAddress(R.string.proxy_address, 5, EMPTY),
         Port(R.string.port, 6, DEFAULT_SIP_PORT),
         Transport(R.string.transport, 7, DEFAULT_PROTOCOL),
-        SendKeepAlive(R.string.send_keepalive, 8, EMPTY);
+        SendKeepAlive(R.string.send_keepalive, 8, EMPTY),
+        AutoRegistration(R.string.auto_registration, 9, EMPTY);
 
         /**
          * @param key The key name of the preference.
@@ -93,14 +95,30 @@
     public void onCreate(Bundle savedInstanceState) {
         Log.v(TAG, "start profile editor");
         super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.sip_settings_ui);
         addPreferencesFromResource(R.xml.sip_edit);
-        SipProfile p = (SipProfile) ((savedInstanceState == null)
+        final SipProfile p = (SipProfile) ((savedInstanceState == null)
                 ? getIntent().getParcelableExtra(SipSettings.KEY_SIP_PROFILE)
                 : savedInstanceState.getParcelable(KEY_PROFILE));
 
         for (PreferenceKey key : PreferenceKey.values()) {
             mPreferences[key.index] = setupPreference(getString(key.text));
         }
+        if (p == null) {
+            findViewById(R.id.add_remove_account_bar)
+                    .setVisibility(View.GONE);
+        } else {
+            Button removeButton =
+                    (Button)findViewById(R.id.add_remove_account_button);
+            removeButton.setText(getString(R.string.remove_sip_account));
+            removeButton.setOnClickListener(
+                    new android.view.View.OnClickListener() {
+                        public void onClick(View v) {
+                            setRemovedProfileAndFinish(p);
+                        }
+                    });
+        }
         loadPreferencesFromProfile(p);
     }
 
@@ -140,6 +158,17 @@
         return super.onKeyDown(keyCode, event);
     }
 
+    private void setRemovedProfileAndFinish(SipProfile p) {
+        try {
+            Intent intent = new Intent(this, SipSettings.class);
+            intent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) p);
+            setResult(RESULT_FIRST_USER, intent);
+            finish();
+        } catch (Exception e) {
+            showAlert(e.getMessage());
+        }
+    }
+
     private void showAlert(String message) {
         new AlertDialog.Builder(this)
                 .setTitle(android.R.string.dialog_alert_title)
@@ -160,10 +189,11 @@
                 value = ((ListPreference)pref).getValue();
             } else if (pref instanceof EditTextPreference) {
                 value = ((EditTextPreference)pref).getText();
+            } else if (pref instanceof CheckBoxPreference) {
+                continue;
             }
             if (TextUtils.isEmpty(value) &&
-                    (pref != mPreferences[PreferenceKey.ProxyAddress.index]) &&
-                    (pref != mPreferences[PreferenceKey.SendKeepAlive.index])) {
+                    (pref != mPreferences[PreferenceKey.ProxyAddress.index])) {
                 showAlert(pref.getTitle() + " "
                         + getString(R.string.empty_alert));
                 return false;
@@ -193,6 +223,8 @@
                     .setDisplayName(getValue(PreferenceKey.DisplayName))
                     .setPort(Integer.parseInt(getValue(PreferenceKey.Port)))
                     .setSendKeepAlive(isChecked(PreferenceKey.SendKeepAlive))
+                    .setAutoRegistration(
+                            isChecked(PreferenceKey.AutoRegistration))
                     .build();
         } catch (Exception e) {
             Log.e(TAG, "Can not create new SipProfile : " + e.getMessage());
@@ -223,7 +255,8 @@
                     if (key == PreferenceKey.Port) {
                         setValue(key,
                                 String.valueOf(meth.invoke(p, (Object[])null)));
-                    } else if (key == PreferenceKey.SendKeepAlive) {
+                    } else if (key == PreferenceKey.SendKeepAlive
+                            || key == PreferenceKey.AutoRegistration) {
                         setCheckBox(key, ((Boolean)
                                 meth.invoke(p, (Object[])null)).booleanValue());
                     } else {
diff --git a/src/com/android/settings/sip/SipSettings.java b/src/com/android/settings/sip/SipSettings.java
index b8088c7..679c55f 100644
--- a/src/com/android/settings/sip/SipSettings.java
+++ b/src/com/android/settings/sip/SipSettings.java
@@ -38,6 +38,7 @@
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.Button;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -61,9 +62,8 @@
     static final String PROFILE_OBJ_FILE = ".pobj";
     static final String PROFILES_DIR = "/profiles/";
 
-    private static final String PREF_AUTO_REG = "auto_registration";
+    private static final String PREF_AUTO_REG = "auto_reg";
     private static final String PREF_SIP_CALL_FIRST = "sip_call_first";
-    private static final String PREF_ADD_SIP = "add_sip_account";
     private static final String PREF_SIP_LIST = "sip_account_list";
     private static final String TAG = "SipSettings";
     private static final String REGISTERED = "REGISTERED";
@@ -114,6 +114,7 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        setContentView(R.layout.sip_settings_ui);
         addPreferencesFromResource(R.xml.sip_setting);
         mProfilesDirectory = getFilesDir().getAbsolutePath() + PROFILES_DIR;
         mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST);
@@ -123,19 +124,16 @@
         // for long-press gesture on a profile preference
         registerForContextMenu(getListView());
 
-        registerForAutoRegistrationListener();
+        registerForGlobalSettingsListener();
 
         updateProfilesStatus();
     }
 
     private void registerForAddSipListener() {
-        PreferenceScreen mAddSip =
-                (PreferenceScreen) findPreference(PREF_ADD_SIP);
-        mAddSip.setOnPreferenceClickListener(
-                new OnPreferenceClickListener() {
-                    public boolean onPreferenceClick(Preference preference) {
+        ((Button) findViewById(R.id.add_remove_account_button))
+                .setOnClickListener(new android.view.View.OnClickListener() {
+                    public void onClick(View v) {
                         startSipEditor(null);
-                        return true;
                     }
                 });
     }
@@ -146,11 +144,11 @@
 
     private class AutoRegistrationClickHandler implements ClickEventCallback {
         public void handle(boolean enabled) {
-            if (enabled) registerAllProfiles();
+            registerEnabledProfiles(enabled);
         }
     }
 
-    private void registerForAutoRegistrationListener() {
+    private void registerForGlobalSettingsListener() {
         mSettingsEditor = getSharedPreferences(
                 SipAutoRegistration.SIP_SHARED_PREFERENCES,
                 Context.MODE_WORLD_READABLE).edit();
@@ -237,15 +235,20 @@
         }
     }
 
-    private void registerAllProfiles() {
+    private void registerEnabledProfiles(boolean enabled) {
         try {
             for (SipProfile p : mSipProfileList) {
-                if (!mSipManager.isRegistered(p.getUriString())) {
-                    registerProfile(p);
+                if (p.getAutoRegistration() == false) continue;
+                if (enabled) {
+                    if (!mSipManager.isRegistered(p.getUriString())) {
+                        registerProfile(p);
+                    }
+                } else {
+                    unRegisterProfile(p);
                 }
             }
         } catch (SipException e) {
-            Log.e(TAG, "Error!registerAllProfiles():", e);
+            Log.e(TAG, "Error!registerEnabledProfiles():", e);
         }
     }
 
@@ -292,56 +295,6 @@
         return menuInfo.position - mSipListContainer.getOrder() - 1;
     }
 
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v,
-            ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, v, menuInfo);
-
-        SipProfile p = getProfile(getProfilePositionFrom(
-                    (AdapterContextMenuInfo) menuInfo));
-        if (p != null) {
-            boolean opened;
-            try {
-                 opened = mSipManager.isOpened(p.getUriString());
-            } catch (SipException e) {
-                Log.e(TAG, "Cannot get status of " + p.getUriString(), e);
-                return;
-            }
-            menu.setHeaderTitle(p.getProfileName());
-            menu.add(0, CONTEXT_MENU_REGISTER_ID, 0,
-                    R.string.sip_menu_register).setEnabled(!opened);
-            menu.add(0, CONTEXT_MENU_UNREGISTER_ID, 0,
-                    R.string.sip_menu_unregister).setEnabled(opened);
-            menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.sip_menu_edit);
-            menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.sip_menu_delete);
-        }
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        SipProfile p = getProfile(getProfilePositionFrom(
-                (AdapterContextMenuInfo) item.getMenuInfo()));
-
-        switch(item.getItemId()) {
-        case CONTEXT_MENU_REGISTER_ID:
-            registerProfile(p);
-            return true;
-        case CONTEXT_MENU_UNREGISTER_ID:
-            unRegisterProfile(p);
-            return true;
-
-        case CONTEXT_MENU_EDIT_ID:
-            startSipEditor(p);
-            return true;
-
-        case CONTEXT_MENU_DELETE_ID:
-            deleteProfile(p);
-            return true;
-        }
-
-        return super.onContextItemSelected(item);
-    }
-
     private void registerProfile(SipProfile profile) {
         if (profile != null) {
             try {
@@ -376,7 +329,7 @@
         file.delete();
     }
 
-    private void deleteProfile(SipProfile p) {
+    void deleteProfile(SipProfile p) {
         mSipProfileList.remove(p);
         SipPreference pref = mSipPreferenceMap.remove(p.getUriString());
         mSipListContainer.removePreference(pref);
@@ -399,14 +352,24 @@
     @Override
     protected void onActivityResult(final int requestCode, final int resultCode,
             final Intent intent) {
-        if (resultCode != RESULT_OK) return;
+        if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return;
         SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE);
         try {
-            saveProfileToStorage(profile);
+            if (resultCode == RESULT_OK) {
+                Log.v(TAG, "New Profile Name:" + profile.getProfileName());
+                saveProfileToStorage(profile);
+                if (((CheckBoxPreference) findPreference
+                        (PREF_AUTO_REG)).isChecked() &&
+                        profile.getAutoRegistration() == true) {
+                    registerProfile(profile);
+                }
+            } else {
+                Log.v(TAG, "Removed Profile Name:" + profile.getProfileName());
+                deleteProfile(profile);
+            }
         } catch (IOException e) {
-            Log.v(TAG, "Can not save the profile : " + e.getMessage());
+            Log.v(TAG, "Can not handle the profile : " + e.getMessage());
         }
-        Log.v(TAG, "New Profile Name is " + profile.getProfileName());
     }
 
     static SipProfile deserialize(File profileObjectFile) throws IOException {
diff --git a/src/com/android/sip/SipSessionGroup.java b/src/com/android/sip/SipSessionGroup.java
index 350d5a2..2924dd5 100644
--- a/src/com/android/sip/SipSessionGroup.java
+++ b/src/com/android/sip/SipSessionGroup.java
@@ -16,6 +16,9 @@
 
 package com.android.sip;
 
+import gov.nist.javax.sip.header.SIPHeaderNames;
+import gov.nist.javax.sip.header.WWWAuthenticate;
+
 import android.net.sip.ISipSession;
 import android.net.sip.ISipSessionListener;
 import android.net.sip.SessionDescription;
@@ -51,6 +54,7 @@
 import javax.sip.SipStack;
 import javax.sip.TimeoutEvent;
 import javax.sip.Transaction;
+import javax.sip.TransactionState;
 import javax.sip.TransactionTerminatedEvent;
 import javax.sip.address.Address;
 import javax.sip.address.SipURI;
@@ -66,6 +70,7 @@
  */
 class SipSessionGroup implements SipListener {
     private static final String TAG = "SipSession";
+    private static final String ANONYMOUS = "anonymous";
     private static final int EXPIRY_TIME = 3600;
 
     private static final EventObject DEREGISTER = new EventObject("Deregister");
@@ -77,6 +82,7 @@
     private SipStack mSipStack;
     private SipHelper mSipHelper;
     private SipProfile mLocalProfile;
+    private String mLastNonce;
 
     // session that processes INVITE requests
     private SipSessionImpl mCallReceiverSession;
@@ -546,8 +552,16 @@
                     return true;
                 case Response.UNAUTHORIZED:
                 case Response.PROXY_AUTHENTICATION_REQUIRED:
-                    mSipHelper.handleChallenge(
-                            (ResponseEvent)evt, mLocalProfile);
+                    String nonce = getNonceFromResponse(response);
+                    if (((nonce != null) && nonce.equals(mLastNonce)) ||
+                            (nonce == mLastNonce)) {
+                        Log.v(TAG, "Incorrect username/password");
+                        reset();
+                        onRegistrationFailed(createCallbackException(response));
+                    } else {
+                        mSipHelper.handleChallenge(event, mLocalProfile);
+                        mLastNonce = nonce;
+                    }
                     return true;
                 default:
                     if (statusCode >= 500) {
@@ -560,6 +574,12 @@
             return false;
         }
 
+        private String getNonceFromResponse(Response response) {
+            WWWAuthenticate authHeader = (WWWAuthenticate)(response.getHeader(
+                    SIPHeaderNames.WWW_AUTHENTICATE));
+            return (authHeader == null) ? null : authHeader.getNonce();
+        }
+
         private boolean readyForCall(EventObject evt) throws SipException {
             // expect MakeCallCommand, RegisterCommand, DEREGISTER
             if (evt instanceof MakeCallCommand) {
@@ -887,7 +907,9 @@
                     (FromHeader) request.getHeader(FromHeader.NAME);
             Address address = fromHeader.getAddress();
             SipURI uri = (SipURI) address.getURI();
-            return new SipProfile.Builder(uri.getUser(), uri.getHost())
+            String username = uri.getUser();
+            if (username == null) username = ANONYMOUS;
+            return new SipProfile.Builder(username, uri.getHost())
                     .setPort(uri.getPort())
                     .setDisplayName(address.getDisplayName())
                     .build();