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