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.MenuItem;
     48 import android.view.View;
     49 import android.widget.Button;
     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     static final String KEY_SIP_PROFILE = "sip_profile";
     65 
     66     private static final String BUTTON_SIP_RECEIVE_CALLS =
     67             "sip_receive_calls_key";
     68     private static final String PREF_SIP_LIST = "sip_account_list";
     69     private static final String TAG = "SipSettings";
     70 
     71     private static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1;
     72 
     73     private PackageManager mPackageManager;
     74     private SipManager mSipManager;
     75     private CallManager mCallManager;
     76     private SipProfileDb mProfileDb;
     77 
     78     private SipProfile mProfile; // profile that's being edited
     79 
     80     private Button mButtonAddSipAccount;
     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         registerForAddSipListener();
    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() != Phone.State.IDLE) {
    172             mButtonAddSipAccount.setEnabled(false);
    173             mButtonSipReceiveCalls.setEnabled(false);
    174         } else {
    175             mButtonAddSipAccount.setEnabled(true);
    176             mButtonSipReceiveCalls.setEnabled(true);
    177         }
    178     }
    179 
    180     @Override
    181     protected void onDestroy() {
    182         super.onDestroy();
    183         unregisterForContextMenu(getListView());
    184     }
    185 
    186     @Override
    187     protected void onActivityResult(final int requestCode, final int resultCode,
    188             final Intent intent) {
    189         if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return;
    190         new Thread() {
    191             public void run() {
    192             try {
    193                 if (mProfile != null) {
    194                     Log.v(TAG, "Removed Profile:" + mProfile.getProfileName());
    195                     deleteProfile(mProfile);
    196                 }
    197 
    198                 SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE);
    199                 if (resultCode == RESULT_OK) {
    200                     Log.v(TAG, "New Profile Name:" + profile.getProfileName());
    201                     addProfile(profile);
    202                 }
    203                 updateProfilesStatus();
    204             } catch (IOException e) {
    205                 Log.v(TAG, "Can not handle the profile : " + e.getMessage());
    206             }
    207         }}.start();
    208     }
    209 
    210     private void registerForAddSipListener() {
    211         mButtonAddSipAccount =
    212                 (Button) findViewById(R.id.add_remove_account_button);
    213         mButtonAddSipAccount.setOnClickListener(
    214                 new android.view.View.OnClickListener() {
    215                     public void onClick(View v) {
    216                         startSipEditor(null);
    217                     }
    218                 });
    219     }
    220 
    221     private void registerForReceiveCallsCheckBox() {
    222         mButtonSipReceiveCalls = (CheckBoxPreference) findPreference
    223                 (BUTTON_SIP_RECEIVE_CALLS);
    224         mButtonSipReceiveCalls.setChecked(
    225                 mSipSharedPreferences.isReceivingCallsEnabled());
    226         mButtonSipReceiveCalls.setOnPreferenceClickListener(
    227                 new OnPreferenceClickListener() {
    228                     public boolean onPreferenceClick(Preference preference) {
    229                         final boolean enabled =
    230                                 ((CheckBoxPreference) preference).isChecked();
    231                         new Thread(new Runnable() {
    232                                 public void run() {
    233                                     handleSipReceiveCallsOption(enabled);
    234                                 }
    235                         }).start();
    236                         return true;
    237                     }
    238                 });
    239     }
    240 
    241     private synchronized void handleSipReceiveCallsOption(boolean enabled) {
    242         mSipSharedPreferences.setReceivingCallsEnabled(enabled);
    243         List<SipProfile> sipProfileList = mProfileDb.retrieveSipProfileList();
    244         for (SipProfile p : sipProfileList) {
    245             String sipUri = p.getUriString();
    246             p = updateAutoRegistrationFlag(p, enabled);
    247             try {
    248                 if (enabled) {
    249                     mSipManager.open(p,
    250                             SipUtil.createIncomingCallPendingIntent(), null);
    251                 } else {
    252                     mSipManager.close(sipUri);
    253                     if (mSipSharedPreferences.isPrimaryAccount(sipUri)) {
    254                         // re-open in order to make calls
    255                         mSipManager.open(p);
    256                     }
    257                 }
    258             } catch (Exception e) {
    259                 Log.e(TAG, "register failed", e);
    260             }
    261         }
    262         updateProfilesStatus();
    263     }
    264 
    265     private SipProfile updateAutoRegistrationFlag(
    266             SipProfile p, boolean enabled) {
    267         SipProfile newProfile = new SipProfile.Builder(p)
    268                 .setAutoRegistration(enabled)
    269                 .build();
    270         try {
    271             mProfileDb.deleteProfile(p);
    272             mProfileDb.saveProfile(newProfile);
    273         } catch (Exception e) {
    274             Log.e(TAG, "updateAutoRegistrationFlag error", e);
    275         }
    276         return newProfile;
    277     }
    278 
    279     private void updateProfilesStatus() {
    280         new Thread(new Runnable() {
    281             public void run() {
    282                 try {
    283                     retrieveSipLists();
    284                 } catch (Exception e) {
    285                     Log.e(TAG, "isRegistered", e);
    286                 }
    287             }
    288         }).start();
    289     }
    290 
    291     private String getProfileName(SipProfile profile) {
    292         String profileName = profile.getProfileName();
    293         if (TextUtils.isEmpty(profileName)) {
    294             profileName = profile.getUserName() + "@" + profile.getSipDomain();
    295         }
    296         return profileName;
    297     }
    298 
    299     private void retrieveSipLists() {
    300         mSipPreferenceMap = new LinkedHashMap<String, SipPreference>();
    301         mSipProfileList = mProfileDb.retrieveSipProfileList();
    302         processActiveProfilesFromSipService();
    303         Collections.sort(mSipProfileList, new Comparator<SipProfile>() {
    304             public int compare(SipProfile p1, SipProfile p2) {
    305                 return getProfileName(p1).compareTo(getProfileName(p2));
    306             }
    307 
    308             public boolean equals(SipProfile p) {
    309                 // not used
    310                 return false;
    311             }
    312         });
    313         mSipListContainer.removeAll();
    314         for (SipProfile p : mSipProfileList) {
    315             addPreferenceFor(p);
    316         }
    317 
    318         if (!mSipSharedPreferences.isReceivingCallsEnabled()) return;
    319         for (SipProfile p : mSipProfileList) {
    320             if (mUid == p.getCallingUid()) {
    321                 try {
    322                     mSipManager.setRegistrationListener(
    323                             p.getUriString(), createRegistrationListener());
    324                 } catch (SipException e) {
    325                     Log.e(TAG, "cannot set registration listener", e);
    326                 }
    327             }
    328         }
    329     }
    330 
    331     private void processActiveProfilesFromSipService() {
    332         SipProfile[] activeList = mSipManager.getListOfProfiles();
    333         for (SipProfile activeProfile : activeList) {
    334             SipProfile profile = getProfileFromList(activeProfile);
    335             if (profile == null) {
    336                 mSipProfileList.add(activeProfile);
    337             } else {
    338                 profile.setCallingUid(activeProfile.getCallingUid());
    339             }
    340         }
    341     }
    342 
    343     private SipProfile getProfileFromList(SipProfile activeProfile) {
    344         for (SipProfile p : mSipProfileList) {
    345             if (p.getUriString().equals(activeProfile.getUriString())) {
    346                 return p;
    347             }
    348         }
    349         return null;
    350     }
    351 
    352     private void addPreferenceFor(SipProfile p) {
    353         String status;
    354         Log.v(TAG, "addPreferenceFor profile uri" + p.getUri());
    355         SipPreference pref = new SipPreference(this, p);
    356         mSipPreferenceMap.put(p.getUriString(), pref);
    357         mSipListContainer.addPreference(pref);
    358 
    359         pref.setOnPreferenceClickListener(
    360                 new Preference.OnPreferenceClickListener() {
    361                     public boolean onPreferenceClick(Preference pref) {
    362                         handleProfileClick(((SipPreference) pref).mProfile);
    363                         return true;
    364                     }
    365                 });
    366     }
    367 
    368     private void handleProfileClick(final SipProfile profile) {
    369         int uid = profile.getCallingUid();
    370         if (uid == mUid || uid == 0) {
    371             startSipEditor(profile);
    372             return;
    373         }
    374         new AlertDialog.Builder(this)
    375                 .setTitle(R.string.alert_dialog_close)
    376                 .setIcon(android.R.drawable.ic_dialog_alert)
    377                 .setPositiveButton(R.string.close_profile,
    378                         new DialogInterface.OnClickListener() {
    379                             public void onClick(DialogInterface dialog, int w) {
    380                                 deleteProfile(profile);
    381                                 unregisterProfile(profile);
    382                             }
    383                         })
    384                 .setNegativeButton(android.R.string.cancel, null)
    385                 .show();
    386     }
    387 
    388     private void unregisterProfile(final SipProfile p) {
    389         // run it on background thread for better UI response
    390         new Thread(new Runnable() {
    391             public void run() {
    392                 try {
    393                     mSipManager.close(p.getUriString());
    394                 } catch (Exception e) {
    395                     Log.e(TAG, "unregister failed, SipService died?", e);
    396                 }
    397             }
    398         }, "unregisterProfile").start();
    399     }
    400 
    401     void deleteProfile(SipProfile p) {
    402         mSipProfileList.remove(p);
    403         SipPreference pref = mSipPreferenceMap.remove(p.getUriString());
    404         mSipListContainer.removePreference(pref);
    405     }
    406 
    407     private void addProfile(SipProfile p) throws IOException {
    408         try {
    409             mSipManager.setRegistrationListener(p.getUriString(),
    410                     createRegistrationListener());
    411         } catch (Exception e) {
    412             Log.e(TAG, "cannot set registration listener", e);
    413         }
    414         mSipProfileList.add(p);
    415         addPreferenceFor(p);
    416     }
    417 
    418     private void startSipEditor(final SipProfile profile) {
    419         mProfile = profile;
    420         Intent intent = new Intent(this, SipEditor.class);
    421         intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile);
    422         startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE);
    423     }
    424 
    425     private void showRegistrationMessage(final String profileUri,
    426             final String message) {
    427         runOnUiThread(new Runnable() {
    428             public void run() {
    429                 SipPreference pref = mSipPreferenceMap.get(profileUri);
    430                 if (pref != null) {
    431                     pref.updateSummary(message);
    432                 }
    433             }
    434         });
    435     }
    436 
    437     private SipRegistrationListener createRegistrationListener() {
    438         return new SipRegistrationListener() {
    439             public void onRegistrationDone(String profileUri, long expiryTime) {
    440                 showRegistrationMessage(profileUri, getString(
    441                         R.string.registration_status_done));
    442             }
    443 
    444             public void onRegistering(String profileUri) {
    445                 showRegistrationMessage(profileUri, getString(
    446                         R.string.registration_status_registering));
    447             }
    448 
    449             public void onRegistrationFailed(String profileUri, int errorCode,
    450                     String message) {
    451                 switch (errorCode) {
    452                     case SipErrorCode.IN_PROGRESS:
    453                         showRegistrationMessage(profileUri, getString(
    454                                 R.string.registration_status_still_trying));
    455                         break;
    456                     case SipErrorCode.INVALID_CREDENTIALS:
    457                         showRegistrationMessage(profileUri, getString(
    458                                 R.string.registration_status_invalid_credentials));
    459                         break;
    460                     case SipErrorCode.SERVER_UNREACHABLE:
    461                         showRegistrationMessage(profileUri, getString(
    462                                 R.string.registration_status_server_unreachable));
    463                         break;
    464                     case SipErrorCode.DATA_CONNECTION_LOST:
    465                         if (SipManager.isSipWifiOnly(getApplicationContext())){
    466                             showRegistrationMessage(profileUri, getString(
    467                                     R.string.registration_status_no_wifi_data));
    468                         } else {
    469                             showRegistrationMessage(profileUri, getString(
    470                                     R.string.registration_status_no_data));
    471                         }
    472                         break;
    473                     case SipErrorCode.CLIENT_ERROR:
    474                         showRegistrationMessage(profileUri, getString(
    475                                 R.string.registration_status_not_running));
    476                         break;
    477                     default:
    478                         showRegistrationMessage(profileUri, getString(
    479                                 R.string.registration_status_failed_try_later,
    480                                 message));
    481                 }
    482             }
    483         };
    484     }
    485 
    486     @Override
    487     public boolean onOptionsItemSelected(MenuItem item) {
    488         final int itemId = item.getItemId();
    489         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
    490             CallFeaturesSetting.goUpToTopLevelSetting(this);
    491             return true;
    492         }
    493         return super.onOptionsItemSelected(item);
    494     }
    495 }
    496