Home | History | Annotate | Download | only in sip
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.phone.sip;
     18 
     19 import com.android.internal.telephony.CallManager;
     20 import com.android.internal.telephony.Phone;
     21 import com.android.phone.R;
     22 import com.android.phone.SipUtil;
     23 
     24 import android.app.AlertDialog;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageManager;
     30 import android.net.sip.SipException;
     31 import android.net.sip.SipErrorCode;
     32 import android.net.sip.SipProfile;
     33 import android.net.sip.SipManager;
     34 import android.net.sip.SipRegistrationListener;
     35 import android.os.Bundle;
     36 import android.os.Parcelable;
     37 import android.os.Process;
     38 import android.preference.CheckBoxPreference;
     39 import android.preference.Preference;
     40 import android.preference.Preference.OnPreferenceClickListener;
     41 import android.preference.PreferenceActivity;
     42 import android.preference.PreferenceCategory;
     43 import android.preference.PreferenceScreen;
     44 import android.text.TextUtils;
     45 import android.util.Log;
     46 import android.view.MenuItem;
     47 import android.view.View;
     48 import android.view.ContextMenu;
     49 import android.view.ContextMenu.ContextMenuInfo;
     50 import android.widget.AdapterView.AdapterContextMenuInfo;
     51 import android.widget.Button;
     52 
     53 import java.io.IOException;
     54 import java.util.Collections;
     55 import java.util.Comparator;
     56 import java.util.LinkedHashMap;
     57 import java.util.List;
     58 import java.util.Map;
     59 
     60 /**
     61  * The PreferenceActivity class for managing sip profile preferences.
     62  */
     63 public class SipSettings extends PreferenceActivity {
     64     public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
     65 
     66     static final String KEY_SIP_PROFILE = "sip_profile";
     67 
     68     private static final String BUTTON_SIP_RECEIVE_CALLS =
     69             "sip_receive_calls_key";
     70     private static final String PREF_SIP_LIST = "sip_account_list";
     71     private static final String TAG = "SipSettings";
     72 
     73     private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1;
     74 
     75     private PackageManager mPackageManager;
     76     private SipManager mSipManager;
     77     private CallManager mCallManager;
     78     private SipProfileDb mProfileDb;
     79 
     80     private SipProfile mProfile; // profile that's being edited
     81 
     82     private Button mButtonAddSipAccount;
     83     private CheckBoxPreference mButtonSipReceiveCalls;
     84     private PreferenceCategory mSipListContainer;
     85     private Map<String, SipPreference> mSipPreferenceMap;
     86     private List<SipProfile> mSipProfileList;
     87     private SipSharedPreferences mSipSharedPreferences;
     88     private int mUid = Process.myUid();
     89 
     90     private class SipPreference extends Preference {
     91         SipProfile mProfile;
     92         SipPreference(Context c, SipProfile p) {
     93             super(c);
     94             setProfile(p);
     95         }
     96 
     97         SipProfile getProfile() {
     98             return mProfile;
     99         }
    100 
    101         void setProfile(SipProfile p) {
    102             mProfile = p;
    103             setTitle(getProfileName(p));
    104             updateSummary(mSipSharedPreferences.isReceivingCallsEnabled()
    105                     ? getString(R.string.registration_status_checking_status)
    106                     : getString(R.string.registration_status_not_receiving));
    107         }
    108 
    109         void updateSummary(String registrationStatus) {
    110             int profileUid = mProfile.getCallingUid();
    111             boolean isPrimary = mProfile.getUriString().equals(
    112                     mSipSharedPreferences.getPrimaryAccount());
    113             Log.v(TAG, "profile uid is " + profileUid + " isPrimary:"
    114                     + isPrimary + " registration:" + registrationStatus
    115                     + " Primary:" + mSipSharedPreferences.getPrimaryAccount()
    116                     + " status:" + registrationStatus);
    117             String summary = "";
    118             if ((profileUid > 0) && (profileUid != mUid)) {
    119                 // from third party apps
    120                 summary = getString(R.string.third_party_account_summary,
    121                         getPackageNameFromUid(profileUid));
    122             } else if (isPrimary) {
    123                 summary = getString(R.string.primary_account_summary_with,
    124                         registrationStatus);
    125             } else {
    126                 summary = registrationStatus;
    127             }
    128             setSummary(summary);
    129         }
    130     }
    131 
    132     private String getPackageNameFromUid(int uid) {
    133         try {
    134             String[] pkgs = mPackageManager.getPackagesForUid(uid);
    135             ApplicationInfo ai =
    136                     mPackageManager.getApplicationInfo(pkgs[0], 0);
    137             return ai.loadLabel(mPackageManager).toString();
    138         } catch (PackageManager.NameNotFoundException e) {
    139             Log.e(TAG, "cannot find name of uid " + uid, e);
    140         }
    141         return "uid:" + uid;
    142     }
    143 
    144     @Override
    145     public void onCreate(Bundle savedInstanceState) {
    146         super.onCreate(savedInstanceState);
    147 
    148         mSipManager = SipManager.newInstance(this);
    149         mSipSharedPreferences = new SipSharedPreferences(this);
    150         mProfileDb = new SipProfileDb(this);
    151 
    152         mPackageManager = getPackageManager();
    153         setContentView(R.layout.sip_settings_ui);
    154         addPreferencesFromResource(R.xml.sip_setting);
    155         mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST);
    156         registerForAddSipListener();
    157         registerForReceiveCallsCheckBox();
    158         mCallManager = CallManager.getInstance();
    159 
    160         updateProfilesStatus();
    161     }
    162 
    163     @Override
    164     public void onResume() {
    165         super.onResume();
    166 
    167         if (mCallManager.getState() != Phone.State.IDLE) {
    168             mButtonAddSipAccount.setEnabled(false);
    169             mButtonSipReceiveCalls.setEnabled(false);
    170         } else {
    171             mButtonAddSipAccount.setEnabled(true);
    172             mButtonSipReceiveCalls.setEnabled(true);
    173         }
    174     }
    175 
    176     @Override
    177     protected void onDestroy() {
    178         super.onDestroy();
    179         unregisterForContextMenu(getListView());
    180     }
    181 
    182     @Override
    183     protected void onActivityResult(final int requestCode, final int resultCode,
    184             final Intent intent) {
    185         if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return;
    186         new Thread() {
    187             public void run() {
    188             try {
    189                 if (mProfile != null) {
    190                     Log.v(TAG, "Removed Profile:" + mProfile.getProfileName());
    191                     deleteProfile(mProfile);
    192                 }
    193 
    194                 SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE);
    195                 if (resultCode == RESULT_OK) {
    196                     Log.v(TAG, "New Profile Name:" + profile.getProfileName());
    197                     addProfile(profile);
    198                 }
    199                 updateProfilesStatus();
    200             } catch (IOException e) {
    201                 Log.v(TAG, "Can not handle the profile : " + e.getMessage());
    202             }
    203         }}.start();
    204     }
    205 
    206     private void registerForAddSipListener() {
    207         mButtonAddSipAccount =
    208                 (Button) findViewById(R.id.add_remove_account_button);
    209         mButtonAddSipAccount.setOnClickListener(
    210                 new android.view.View.OnClickListener() {
    211                     public void onClick(View v) {
    212                         startSipEditor(null);
    213                     }
    214                 });
    215     }
    216 
    217     private void registerForReceiveCallsCheckBox() {
    218         mButtonSipReceiveCalls = (CheckBoxPreference) findPreference
    219                 (BUTTON_SIP_RECEIVE_CALLS);
    220         mButtonSipReceiveCalls.setChecked(
    221                 mSipSharedPreferences.isReceivingCallsEnabled());
    222         mButtonSipReceiveCalls.setOnPreferenceClickListener(
    223                 new OnPreferenceClickListener() {
    224                     public boolean onPreferenceClick(Preference preference) {
    225                         final boolean enabled =
    226                                 ((CheckBoxPreference) preference).isChecked();
    227                         new Thread(new Runnable() {
    228                                 public void run() {
    229                                     handleSipReceiveCallsOption(enabled);
    230                                 }
    231                         }).start();
    232                         return true;
    233                     }
    234                 });
    235     }
    236 
    237     private synchronized void handleSipReceiveCallsOption(boolean enabled) {
    238         mSipSharedPreferences.setReceivingCallsEnabled(enabled);
    239         List<SipProfile> sipProfileList = mProfileDb.retrieveSipProfileList();
    240         for (SipProfile p : sipProfileList) {
    241             String sipUri = p.getUriString();
    242             p = updateAutoRegistrationFlag(p, enabled);
    243             try {
    244                 if (enabled) {
    245                     mSipManager.open(p,
    246                             SipUtil.createIncomingCallPendingIntent(), null);
    247                 } else {
    248                     mSipManager.close(sipUri);
    249                     if (mSipSharedPreferences.isPrimaryAccount(sipUri)) {
    250                         // re-open in order to make calls
    251                         mSipManager.open(p);
    252                     }
    253                 }
    254             } catch (Exception e) {
    255                 Log.e(TAG, "register failed", e);
    256             }
    257         }
    258         updateProfilesStatus();
    259     }
    260 
    261     private SipProfile updateAutoRegistrationFlag(
    262             SipProfile p, boolean enabled) {
    263         SipProfile newProfile = new SipProfile.Builder(p)
    264                 .setAutoRegistration(enabled)
    265                 .build();
    266         try {
    267             mProfileDb.deleteProfile(p);
    268             mProfileDb.saveProfile(newProfile);
    269         } catch (Exception e) {
    270             Log.e(TAG, "updateAutoRegistrationFlag error", e);
    271         }
    272         return newProfile;
    273     }
    274 
    275     private void updateProfilesStatus() {
    276         new Thread(new Runnable() {
    277             public void run() {
    278                 try {
    279                     retrieveSipLists();
    280                 } catch (Exception e) {
    281                     Log.e(TAG, "isRegistered", e);
    282                 }
    283             }
    284         }).start();
    285     }
    286 
    287     private String getProfileName(SipProfile profile) {
    288         String profileName = profile.getProfileName();
    289         if (TextUtils.isEmpty(profileName)) {
    290             profileName = profile.getUserName() + "@" + profile.getSipDomain();
    291         }
    292         return profileName;
    293     }
    294 
    295     private void retrieveSipLists() {
    296         mSipPreferenceMap = new LinkedHashMap<String, SipPreference>();
    297         mSipProfileList = mProfileDb.retrieveSipProfileList();
    298         processActiveProfilesFromSipService();
    299         Collections.sort(mSipProfileList, new Comparator<SipProfile>() {
    300             public int compare(SipProfile p1, SipProfile p2) {
    301                 return getProfileName(p1).compareTo(getProfileName(p2));
    302             }
    303 
    304             public boolean equals(SipProfile p) {
    305                 // not used
    306                 return false;
    307             }
    308         });
    309         mSipListContainer.removeAll();
    310         for (SipProfile p : mSipProfileList) {
    311             addPreferenceFor(p);
    312         }
    313 
    314         if (!mSipSharedPreferences.isReceivingCallsEnabled()) return;
    315         for (SipProfile p : mSipProfileList) {
    316             if (mUid == p.getCallingUid()) {
    317                 try {
    318                     mSipManager.setRegistrationListener(
    319                             p.getUriString(), createRegistrationListener());
    320                 } catch (SipException e) {
    321                     Log.e(TAG, "cannot set registration listener", e);
    322                 }
    323             }
    324         }
    325     }
    326 
    327     private void processActiveProfilesFromSipService() {
    328         SipProfile[] activeList = mSipManager.getListOfProfiles();
    329         for (SipProfile activeProfile : activeList) {
    330             SipProfile profile = getProfileFromList(activeProfile);
    331             if (profile == null) {
    332                 mSipProfileList.add(activeProfile);
    333             } else {
    334                 profile.setCallingUid(activeProfile.getCallingUid());
    335             }
    336         }
    337     }
    338 
    339     private SipProfile getProfileFromList(SipProfile activeProfile) {
    340         for (SipProfile p : mSipProfileList) {
    341             if (p.getUriString().equals(activeProfile.getUriString())) {
    342                 return p;
    343             }
    344         }
    345         return null;
    346     }
    347 
    348     private void addPreferenceFor(SipProfile p) {
    349         String status;
    350         Log.v(TAG, "addPreferenceFor profile uri" + p.getUri());
    351         SipPreference pref = new SipPreference(this, p);
    352         mSipPreferenceMap.put(p.getUriString(), pref);
    353         mSipListContainer.addPreference(pref);
    354 
    355         pref.setOnPreferenceClickListener(
    356                 new Preference.OnPreferenceClickListener() {
    357                     public boolean onPreferenceClick(Preference pref) {
    358                         handleProfileClick(((SipPreference) pref).mProfile);
    359                         return true;
    360                     }
    361                 });
    362     }
    363 
    364     private void handleProfileClick(final SipProfile profile) {
    365         int uid = profile.getCallingUid();
    366         if (uid == mUid || uid == 0) {
    367             startSipEditor(profile);
    368             return;
    369         }
    370         new AlertDialog.Builder(this)
    371                 .setTitle(R.string.alert_dialog_close)
    372                 .setIcon(android.R.drawable.ic_dialog_alert)
    373                 .setPositiveButton(R.string.close_profile,
    374                         new DialogInterface.OnClickListener() {
    375                             public void onClick(DialogInterface dialog, int w) {
    376                                 deleteProfile(profile);
    377                                 unregisterProfile(profile);
    378                             }
    379                         })
    380                 .setNegativeButton(android.R.string.cancel, null)
    381                 .show();
    382     }
    383 
    384     private void unregisterProfile(final SipProfile p) {
    385         // run it on background thread for better UI response
    386         new Thread(new Runnable() {
    387             public void run() {
    388                 try {
    389                     mSipManager.close(p.getUriString());
    390                 } catch (Exception e) {
    391                     Log.e(TAG, "unregister failed, SipService died?", e);
    392                 }
    393             }
    394         }, "unregisterProfile").start();
    395     }
    396 
    397     void deleteProfile(SipProfile p) {
    398         mSipProfileList.remove(p);
    399         SipPreference pref = mSipPreferenceMap.remove(p.getUriString());
    400         mSipListContainer.removePreference(pref);
    401     }
    402 
    403     private void addProfile(SipProfile p) throws IOException {
    404         try {
    405             mSipManager.setRegistrationListener(p.getUriString(),
    406                     createRegistrationListener());
    407         } catch (Exception e) {
    408             Log.e(TAG, "cannot set registration listener", e);
    409         }
    410         mSipProfileList.add(p);
    411         addPreferenceFor(p);
    412     }
    413 
    414     private void startSipEditor(final SipProfile profile) {
    415         mProfile = profile;
    416         Intent intent = new Intent(this, SipEditor.class);
    417         intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile);
    418         startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE);
    419     }
    420 
    421     private void showRegistrationMessage(final String profileUri,
    422             final String message) {
    423         runOnUiThread(new Runnable() {
    424             public void run() {
    425                 SipPreference pref = mSipPreferenceMap.get(profileUri);
    426                 if (pref != null) {
    427                     pref.updateSummary(message);
    428                 }
    429             }
    430         });
    431     }
    432 
    433     private SipRegistrationListener createRegistrationListener() {
    434         return new SipRegistrationListener() {
    435             public void onRegistrationDone(String profileUri, long expiryTime) {
    436                 showRegistrationMessage(profileUri, getString(
    437                         R.string.registration_status_done));
    438             }
    439 
    440             public void onRegistering(String profileUri) {
    441                 showRegistrationMessage(profileUri, getString(
    442                         R.string.registration_status_registering));
    443             }
    444 
    445             public void onRegistrationFailed(String profileUri, int errorCode,
    446                     String message) {
    447                 switch (errorCode) {
    448                     case SipErrorCode.IN_PROGRESS:
    449                         showRegistrationMessage(profileUri, getString(
    450                                 R.string.registration_status_still_trying));
    451                         break;
    452                     case SipErrorCode.INVALID_CREDENTIALS:
    453                         showRegistrationMessage(profileUri, getString(
    454                                 R.string.registration_status_invalid_credentials));
    455                         break;
    456                     case SipErrorCode.SERVER_UNREACHABLE:
    457                         showRegistrationMessage(profileUri, getString(
    458                                 R.string.registration_status_server_unreachable));
    459                         break;
    460                     case SipErrorCode.DATA_CONNECTION_LOST:
    461                         if (SipManager.isSipWifiOnly(getApplicationContext())){
    462                             showRegistrationMessage(profileUri, getString(
    463                                     R.string.registration_status_no_wifi_data));
    464                         } else {
    465                             showRegistrationMessage(profileUri, getString(
    466                                     R.string.registration_status_no_data));
    467                         }
    468                         break;
    469                     case SipErrorCode.CLIENT_ERROR:
    470                         showRegistrationMessage(profileUri, getString(
    471                                 R.string.registration_status_not_running));
    472                         break;
    473                     default:
    474                         showRegistrationMessage(profileUri, getString(
    475                                 R.string.registration_status_failed_try_later,
    476                                 message));
    477                 }
    478             }
    479         };
    480     }
    481 }
    482