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