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