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