SipSettings.java revision 788959e2d798da2d8a34cf89779421966d200f3d
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 17package com.android.services.telephony.sip; 18 19import com.android.internal.telephony.CallManager; 20import com.android.internal.telephony.Phone; 21import com.android.internal.telephony.PhoneConstants; 22 23import android.app.ActionBar; 24import android.app.AlertDialog; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.content.pm.ApplicationInfo; 29import android.content.pm.PackageManager; 30import android.net.sip.SipErrorCode; 31import android.net.sip.SipException; 32import android.net.sip.SipManager; 33import android.net.sip.SipProfile; 34import android.net.sip.SipRegistrationListener; 35import android.os.Bundle; 36import android.os.Parcelable; 37import android.os.Process; 38import android.preference.CheckBoxPreference; 39import android.preference.Preference; 40import android.preference.Preference.OnPreferenceClickListener; 41import android.preference.PreferenceActivity; 42import android.preference.PreferenceCategory; 43import android.text.TextUtils; 44import android.util.Log; 45import android.view.Menu; 46import android.view.MenuItem; 47 48import java.io.IOException; 49import java.util.Collections; 50import java.util.Comparator; 51import java.util.LinkedHashMap; 52import java.util.List; 53import java.util.Map; 54 55/** 56 * The PreferenceActivity class for managing sip profile preferences. 57 */ 58public class SipSettings extends PreferenceActivity { 59 private static final String PREFIX = "[SipSettings] "; 60 private static final boolean VERBOSE = true; /* STOP SHIP if true */ 61 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 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 if (VERBOSE) { 112 log("SipPreference.updateSummary, profile uid: " + profileUid + 113 " isPrimary: " + isPrimary + 114 " registration: " + registrationStatus + 115 " Primary: " + mSipSharedPreferences.getPrimaryAccount() + 116 " status: " + registrationStatus); 117 } 118 String summary = ""; 119 if ((profileUid > 0) && (profileUid != mUid)) { 120 // from third party apps 121 summary = getString(R.string.third_party_account_summary, 122 getPackageNameFromUid(profileUid)); 123 } else if (isPrimary) { 124 summary = getString(R.string.primary_account_summary_with, 125 registrationStatus); 126 } else { 127 summary = registrationStatus; 128 } 129 setSummary(summary); 130 } 131 } 132 133 private String getPackageNameFromUid(int uid) { 134 try { 135 String[] pkgs = mPackageManager.getPackagesForUid(uid); 136 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkgs[0], 0); 137 return ai.loadLabel(mPackageManager).toString(); 138 } catch (PackageManager.NameNotFoundException e) { 139 log("getPackageNameFromUid, cannot find name of uid: " + uid + ", exception: " + 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 registerForReceiveCallsCheckBox(); 157 mCallManager = CallManager.getInstance(); 158 159 updateProfilesStatus(); 160 161 ActionBar actionBar = getActionBar(); 162 if (actionBar != null) { 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 if (VERBOSE) log("onActivityResult, remove: " + mProfile.getProfileName()); 194 deleteProfile(mProfile); 195 } 196 197 SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE); 198 if (resultCode == RESULT_OK) { 199 if (VERBOSE) log("onActivityResult, new: " + profile.getProfileName()); 200 addProfile(profile); 201 } 202 updateProfilesStatus(); 203 } catch (IOException e) { 204 log("onActivityResult, can not handle the profile: " + e); 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 = ((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, SipUtil.createIncomingCallPendingIntent(this), null); 238 } else { 239 mSipManager.close(sipUri); 240 if (mSipSharedPreferences.isPrimaryAccount(sipUri)) { 241 // re-open in order to make calls 242 mSipManager.open(p); 243 } 244 } 245 } catch (Exception e) { 246 log("handleSipReceiveCallsOption, register failed: " + e); 247 } 248 } 249 updateProfilesStatus(); 250 } 251 252 private SipProfile updateAutoRegistrationFlag( 253 SipProfile p, boolean enabled) { 254 SipProfile newProfile = new SipProfile.Builder(p) 255 .setAutoRegistration(enabled) 256 .build(); 257 try { 258 mProfileDb.deleteProfile(p); 259 mProfileDb.saveProfile(newProfile); 260 } catch (Exception e) { 261 log("updateAutoRegistrationFlag, exception: " + e); 262 } 263 return newProfile; 264 } 265 266 private void updateProfilesStatus() { 267 new Thread(new Runnable() { 268 @Override 269 public void run() { 270 try { 271 retrieveSipLists(); 272 } catch (Exception e) { 273 log("updateProfilesStatus, exception: " + e); 274 } 275 } 276 }).start(); 277 } 278 279 private String getProfileName(SipProfile profile) { 280 String profileName = profile.getProfileName(); 281 if (TextUtils.isEmpty(profileName)) { 282 profileName = profile.getUserName() + "@" + profile.getSipDomain(); 283 } 284 return profileName; 285 } 286 287 private void retrieveSipLists() { 288 mSipPreferenceMap = new LinkedHashMap<String, SipPreference>(); 289 mSipProfileList = mProfileDb.retrieveSipProfileList(); 290 processActiveProfilesFromSipService(); 291 Collections.sort(mSipProfileList, new Comparator<SipProfile>() { 292 @Override 293 public int compare(SipProfile p1, SipProfile p2) { 294 return getProfileName(p1).compareTo(getProfileName(p2)); 295 } 296 297 public boolean equals(SipProfile p) { 298 // not used 299 return false; 300 } 301 }); 302 mSipListContainer.removeAll(); 303 for (SipProfile p : mSipProfileList) { 304 addPreferenceFor(p); 305 } 306 307 if (!mSipSharedPreferences.isReceivingCallsEnabled()) return; 308 for (SipProfile p : mSipProfileList) { 309 if (mUid == p.getCallingUid()) { 310 try { 311 mSipManager.setRegistrationListener( 312 p.getUriString(), createRegistrationListener()); 313 } catch (SipException e) { 314 log("retrieveSipLists, cannot set registration listener: " + e); 315 } 316 } 317 } 318 } 319 320 private void processActiveProfilesFromSipService() { 321 SipProfile[] activeList = mSipManager.getListOfProfiles(); 322 for (SipProfile activeProfile : activeList) { 323 SipProfile profile = getProfileFromList(activeProfile); 324 if (profile == null) { 325 mSipProfileList.add(activeProfile); 326 } else { 327 profile.setCallingUid(activeProfile.getCallingUid()); 328 } 329 } 330 } 331 332 private SipProfile getProfileFromList(SipProfile activeProfile) { 333 for (SipProfile p : mSipProfileList) { 334 if (p.getUriString().equals(activeProfile.getUriString())) { 335 return p; 336 } 337 } 338 return null; 339 } 340 341 private void addPreferenceFor(SipProfile p) { 342 String status; 343 if (VERBOSE) log("addPreferenceFor, profile uri: " + p.getUri()); 344 SipPreference pref = new SipPreference(this, p); 345 mSipPreferenceMap.put(p.getUriString(), pref); 346 mSipListContainer.addPreference(pref); 347 348 pref.setOnPreferenceClickListener( 349 new Preference.OnPreferenceClickListener() { 350 @Override 351 public boolean onPreferenceClick(Preference pref) { 352 handleProfileClick(((SipPreference) pref).mProfile); 353 return true; 354 } 355 }); 356 } 357 358 private void handleProfileClick(final SipProfile profile) { 359 int uid = profile.getCallingUid(); 360 if (uid == mUid || uid == 0) { 361 startSipEditor(profile); 362 return; 363 } 364 new AlertDialog.Builder(this) 365 .setTitle(R.string.alert_dialog_close) 366 .setIconAttribute(android.R.attr.alertDialogIcon) 367 .setPositiveButton(R.string.close_profile, 368 new DialogInterface.OnClickListener() { 369 @Override 370 public void onClick(DialogInterface dialog, int w) { 371 deleteProfile(profile); 372 unregisterProfile(profile); 373 } 374 }) 375 .setNegativeButton(android.R.string.cancel, null) 376 .show(); 377 } 378 379 private void unregisterProfile(final SipProfile p) { 380 // run it on background thread for better UI response 381 new Thread(new Runnable() { 382 @Override 383 public void run() { 384 try { 385 mSipManager.close(p.getUriString()); 386 } catch (Exception e) { 387 log("unregisterProfile, unregister failed, SipService died? Exception: " + e); 388 } 389 } 390 }, "unregisterProfile").start(); 391 } 392 393 void deleteProfile(SipProfile p) { 394 mSipProfileList.remove(p); 395 SipPreference pref = mSipPreferenceMap.remove(p.getUriString()); 396 mSipListContainer.removePreference(pref); 397 } 398 399 private void addProfile(SipProfile p) throws IOException { 400 try { 401 mSipManager.setRegistrationListener(p.getUriString(), 402 createRegistrationListener()); 403 } catch (Exception e) { 404 log("addProfile, cannot set registration listener: " + e); 405 } 406 mSipProfileList.add(p); 407 addPreferenceFor(p); 408 } 409 410 private void startSipEditor(final SipProfile profile) { 411 mProfile = profile; 412 Intent intent = new Intent(this, SipEditor.class); 413 intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile); 414 startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE); 415 } 416 417 private void showRegistrationMessage(final String profileUri, 418 final String message) { 419 runOnUiThread(new Runnable() { 420 @Override 421 public void run() { 422 SipPreference pref = mSipPreferenceMap.get(profileUri); 423 if (pref != null) { 424 pref.updateSummary(message); 425 } 426 } 427 }); 428 } 429 430 private SipRegistrationListener createRegistrationListener() { 431 return new SipRegistrationListener() { 432 @Override 433 public void onRegistrationDone(String profileUri, long expiryTime) { 434 showRegistrationMessage(profileUri, getString( 435 R.string.registration_status_done)); 436 } 437 438 @Override 439 public void onRegistering(String profileUri) { 440 showRegistrationMessage(profileUri, getString( 441 R.string.registration_status_registering)); 442 } 443 444 @Override 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 @Override 483 public boolean onCreateOptionsMenu(Menu menu) { 484 super.onCreateOptionsMenu(menu); 485 menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_sip_account) 486 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 487 return true; 488 } 489 490 @Override 491 public boolean onPrepareOptionsMenu(Menu menu) { 492 menu.findItem(MENU_ADD_ACCOUNT).setEnabled( 493 mCallManager.getState() == PhoneConstants.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 MENU_ADD_ACCOUNT: { 502 startSipEditor(null); 503 return true; 504 } 505 } 506 return super.onOptionsItemSelected(item); 507 } 508 509 private static void log(String msg) { 510 Log.d(SipUtil.LOG_TAG, PREFIX + msg); 511 } 512} 513