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