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.R; 22 import com.android.phone.SipUtil; 23 24 import android.app.AlertDialog; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageManager; 30 import android.net.sip.SipException; 31 import android.net.sip.SipErrorCode; 32 import android.net.sip.SipProfile; 33 import android.net.sip.SipManager; 34 import android.net.sip.SipRegistrationListener; 35 import android.os.Bundle; 36 import android.os.Parcelable; 37 import android.os.Process; 38 import android.preference.CheckBoxPreference; 39 import android.preference.Preference; 40 import android.preference.Preference.OnPreferenceClickListener; 41 import android.preference.PreferenceActivity; 42 import android.preference.PreferenceCategory; 43 import android.preference.PreferenceScreen; 44 import android.text.TextUtils; 45 import android.util.Log; 46 import android.view.MenuItem; 47 import android.view.View; 48 import android.view.ContextMenu; 49 import android.view.ContextMenu.ContextMenuInfo; 50 import android.widget.AdapterView.AdapterContextMenuInfo; 51 import android.widget.Button; 52 53 import java.io.IOException; 54 import java.util.Collections; 55 import java.util.Comparator; 56 import java.util.LinkedHashMap; 57 import java.util.List; 58 import java.util.Map; 59 60 /** 61 * The PreferenceActivity class for managing sip profile preferences. 62 */ 63 public class SipSettings extends PreferenceActivity { 64 public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; 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 Button mButtonAddSipAccount; 83 private CheckBoxPreference mButtonSipReceiveCalls; 84 private PreferenceCategory mSipListContainer; 85 private Map<String, SipPreference> mSipPreferenceMap; 86 private List<SipProfile> mSipProfileList; 87 private SipSharedPreferences mSipSharedPreferences; 88 private int mUid = Process.myUid(); 89 90 private class SipPreference extends Preference { 91 SipProfile mProfile; 92 SipPreference(Context c, SipProfile p) { 93 super(c); 94 setProfile(p); 95 } 96 97 SipProfile getProfile() { 98 return mProfile; 99 } 100 101 void setProfile(SipProfile p) { 102 mProfile = p; 103 setTitle(getProfileName(p)); 104 updateSummary(mSipSharedPreferences.isReceivingCallsEnabled() 105 ? getString(R.string.registration_status_checking_status) 106 : getString(R.string.registration_status_not_receiving)); 107 } 108 109 void updateSummary(String registrationStatus) { 110 int profileUid = mProfile.getCallingUid(); 111 boolean isPrimary = mProfile.getUriString().equals( 112 mSipSharedPreferences.getPrimaryAccount()); 113 Log.v(TAG, "profile uid is " + profileUid + " isPrimary:" 114 + isPrimary + " registration:" + registrationStatus 115 + " Primary:" + mSipSharedPreferences.getPrimaryAccount() 116 + " status:" + registrationStatus); 117 String summary = ""; 118 if ((profileUid > 0) && (profileUid != mUid)) { 119 // from third party apps 120 summary = getString(R.string.third_party_account_summary, 121 getPackageNameFromUid(profileUid)); 122 } else if (isPrimary) { 123 summary = getString(R.string.primary_account_summary_with, 124 registrationStatus); 125 } else { 126 summary = registrationStatus; 127 } 128 setSummary(summary); 129 } 130 } 131 132 private String getPackageNameFromUid(int uid) { 133 try { 134 String[] pkgs = mPackageManager.getPackagesForUid(uid); 135 ApplicationInfo ai = 136 mPackageManager.getApplicationInfo(pkgs[0], 0); 137 return ai.loadLabel(mPackageManager).toString(); 138 } catch (PackageManager.NameNotFoundException e) { 139 Log.e(TAG, "cannot find name of uid " + uid, 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 registerForAddSipListener(); 157 registerForReceiveCallsCheckBox(); 158 mCallManager = CallManager.getInstance(); 159 160 updateProfilesStatus(); 161 } 162 163 @Override 164 public void onResume() { 165 super.onResume(); 166 167 if (mCallManager.getState() != Phone.State.IDLE) { 168 mButtonAddSipAccount.setEnabled(false); 169 mButtonSipReceiveCalls.setEnabled(false); 170 } else { 171 mButtonAddSipAccount.setEnabled(true); 172 mButtonSipReceiveCalls.setEnabled(true); 173 } 174 } 175 176 @Override 177 protected void onDestroy() { 178 super.onDestroy(); 179 unregisterForContextMenu(getListView()); 180 } 181 182 @Override 183 protected void onActivityResult(final int requestCode, final int resultCode, 184 final Intent intent) { 185 if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return; 186 new Thread() { 187 public void run() { 188 try { 189 if (mProfile != null) { 190 Log.v(TAG, "Removed Profile:" + mProfile.getProfileName()); 191 deleteProfile(mProfile); 192 } 193 194 SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE); 195 if (resultCode == RESULT_OK) { 196 Log.v(TAG, "New Profile Name:" + profile.getProfileName()); 197 addProfile(profile); 198 } 199 updateProfilesStatus(); 200 } catch (IOException e) { 201 Log.v(TAG, "Can not handle the profile : " + e.getMessage()); 202 } 203 }}.start(); 204 } 205 206 private void registerForAddSipListener() { 207 mButtonAddSipAccount = 208 (Button) findViewById(R.id.add_remove_account_button); 209 mButtonAddSipAccount.setOnClickListener( 210 new android.view.View.OnClickListener() { 211 public void onClick(View v) { 212 startSipEditor(null); 213 } 214 }); 215 } 216 217 private void registerForReceiveCallsCheckBox() { 218 mButtonSipReceiveCalls = (CheckBoxPreference) findPreference 219 (BUTTON_SIP_RECEIVE_CALLS); 220 mButtonSipReceiveCalls.setChecked( 221 mSipSharedPreferences.isReceivingCallsEnabled()); 222 mButtonSipReceiveCalls.setOnPreferenceClickListener( 223 new OnPreferenceClickListener() { 224 public boolean onPreferenceClick(Preference preference) { 225 final boolean enabled = 226 ((CheckBoxPreference) preference).isChecked(); 227 new Thread(new Runnable() { 228 public void run() { 229 handleSipReceiveCallsOption(enabled); 230 } 231 }).start(); 232 return true; 233 } 234 }); 235 } 236 237 private synchronized void handleSipReceiveCallsOption(boolean enabled) { 238 mSipSharedPreferences.setReceivingCallsEnabled(enabled); 239 List<SipProfile> sipProfileList = mProfileDb.retrieveSipProfileList(); 240 for (SipProfile p : sipProfileList) { 241 String sipUri = p.getUriString(); 242 p = updateAutoRegistrationFlag(p, enabled); 243 try { 244 if (enabled) { 245 mSipManager.open(p, 246 SipUtil.createIncomingCallPendingIntent(), null); 247 } else { 248 mSipManager.close(sipUri); 249 if (mSipSharedPreferences.isPrimaryAccount(sipUri)) { 250 // re-open in order to make calls 251 mSipManager.open(p); 252 } 253 } 254 } catch (Exception e) { 255 Log.e(TAG, "register failed", e); 256 } 257 } 258 updateProfilesStatus(); 259 } 260 261 private SipProfile updateAutoRegistrationFlag( 262 SipProfile p, boolean enabled) { 263 SipProfile newProfile = new SipProfile.Builder(p) 264 .setAutoRegistration(enabled) 265 .build(); 266 try { 267 mProfileDb.deleteProfile(p); 268 mProfileDb.saveProfile(newProfile); 269 } catch (Exception e) { 270 Log.e(TAG, "updateAutoRegistrationFlag error", e); 271 } 272 return newProfile; 273 } 274 275 private void updateProfilesStatus() { 276 new Thread(new Runnable() { 277 public void run() { 278 try { 279 retrieveSipLists(); 280 } catch (Exception e) { 281 Log.e(TAG, "isRegistered", e); 282 } 283 } 284 }).start(); 285 } 286 287 private String getProfileName(SipProfile profile) { 288 String profileName = profile.getProfileName(); 289 if (TextUtils.isEmpty(profileName)) { 290 profileName = profile.getUserName() + "@" + profile.getSipDomain(); 291 } 292 return profileName; 293 } 294 295 private void retrieveSipLists() { 296 mSipPreferenceMap = new LinkedHashMap<String, SipPreference>(); 297 mSipProfileList = mProfileDb.retrieveSipProfileList(); 298 processActiveProfilesFromSipService(); 299 Collections.sort(mSipProfileList, new Comparator<SipProfile>() { 300 public int compare(SipProfile p1, SipProfile p2) { 301 return getProfileName(p1).compareTo(getProfileName(p2)); 302 } 303 304 public boolean equals(SipProfile p) { 305 // not used 306 return false; 307 } 308 }); 309 mSipListContainer.removeAll(); 310 for (SipProfile p : mSipProfileList) { 311 addPreferenceFor(p); 312 } 313 314 if (!mSipSharedPreferences.isReceivingCallsEnabled()) return; 315 for (SipProfile p : mSipProfileList) { 316 if (mUid == p.getCallingUid()) { 317 try { 318 mSipManager.setRegistrationListener( 319 p.getUriString(), createRegistrationListener()); 320 } catch (SipException e) { 321 Log.e(TAG, "cannot set registration listener", e); 322 } 323 } 324 } 325 } 326 327 private void processActiveProfilesFromSipService() { 328 SipProfile[] activeList = mSipManager.getListOfProfiles(); 329 for (SipProfile activeProfile : activeList) { 330 SipProfile profile = getProfileFromList(activeProfile); 331 if (profile == null) { 332 mSipProfileList.add(activeProfile); 333 } else { 334 profile.setCallingUid(activeProfile.getCallingUid()); 335 } 336 } 337 } 338 339 private SipProfile getProfileFromList(SipProfile activeProfile) { 340 for (SipProfile p : mSipProfileList) { 341 if (p.getUriString().equals(activeProfile.getUriString())) { 342 return p; 343 } 344 } 345 return null; 346 } 347 348 private void addPreferenceFor(SipProfile p) { 349 String status; 350 Log.v(TAG, "addPreferenceFor profile uri" + p.getUri()); 351 SipPreference pref = new SipPreference(this, p); 352 mSipPreferenceMap.put(p.getUriString(), pref); 353 mSipListContainer.addPreference(pref); 354 355 pref.setOnPreferenceClickListener( 356 new Preference.OnPreferenceClickListener() { 357 public boolean onPreferenceClick(Preference pref) { 358 handleProfileClick(((SipPreference) pref).mProfile); 359 return true; 360 } 361 }); 362 } 363 364 private void handleProfileClick(final SipProfile profile) { 365 int uid = profile.getCallingUid(); 366 if (uid == mUid || uid == 0) { 367 startSipEditor(profile); 368 return; 369 } 370 new AlertDialog.Builder(this) 371 .setTitle(R.string.alert_dialog_close) 372 .setIcon(android.R.drawable.ic_dialog_alert) 373 .setPositiveButton(R.string.close_profile, 374 new DialogInterface.OnClickListener() { 375 public void onClick(DialogInterface dialog, int w) { 376 deleteProfile(profile); 377 unregisterProfile(profile); 378 } 379 }) 380 .setNegativeButton(android.R.string.cancel, null) 381 .show(); 382 } 383 384 private void unregisterProfile(final SipProfile p) { 385 // run it on background thread for better UI response 386 new Thread(new Runnable() { 387 public void run() { 388 try { 389 mSipManager.close(p.getUriString()); 390 } catch (Exception e) { 391 Log.e(TAG, "unregister failed, SipService died?", e); 392 } 393 } 394 }, "unregisterProfile").start(); 395 } 396 397 void deleteProfile(SipProfile p) { 398 mSipProfileList.remove(p); 399 SipPreference pref = mSipPreferenceMap.remove(p.getUriString()); 400 mSipListContainer.removePreference(pref); 401 } 402 403 private void addProfile(SipProfile p) throws IOException { 404 try { 405 mSipManager.setRegistrationListener(p.getUriString(), 406 createRegistrationListener()); 407 } catch (Exception e) { 408 Log.e(TAG, "cannot set registration listener", e); 409 } 410 mSipProfileList.add(p); 411 addPreferenceFor(p); 412 } 413 414 private void startSipEditor(final SipProfile profile) { 415 mProfile = profile; 416 Intent intent = new Intent(this, SipEditor.class); 417 intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile); 418 startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE); 419 } 420 421 private void showRegistrationMessage(final String profileUri, 422 final String message) { 423 runOnUiThread(new Runnable() { 424 public void run() { 425 SipPreference pref = mSipPreferenceMap.get(profileUri); 426 if (pref != null) { 427 pref.updateSummary(message); 428 } 429 } 430 }); 431 } 432 433 private SipRegistrationListener createRegistrationListener() { 434 return new SipRegistrationListener() { 435 public void onRegistrationDone(String profileUri, long expiryTime) { 436 showRegistrationMessage(profileUri, getString( 437 R.string.registration_status_done)); 438 } 439 440 public void onRegistering(String profileUri) { 441 showRegistrationMessage(profileUri, getString( 442 R.string.registration_status_registering)); 443 } 444 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