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.services.telephony.sip; 18 19 import android.app.ActionBar; 20 import android.app.AlertDialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.net.sip.SipErrorCode; 27 import android.net.sip.SipException; 28 import android.net.sip.SipManager; 29 import android.net.sip.SipProfile; 30 import android.net.sip.SipRegistrationListener; 31 import android.os.Bundle; 32 import android.os.Parcelable; 33 import android.os.Process; 34 import android.preference.Preference; 35 import android.preference.PreferenceActivity; 36 import android.preference.PreferenceCategory; 37 import android.text.TextUtils; 38 import android.util.Log; 39 import android.view.Menu; 40 import android.view.MenuItem; 41 42 import com.android.phone.R; 43 44 import java.io.IOException; 45 import java.util.Collections; 46 import java.util.Comparator; 47 import java.util.LinkedHashMap; 48 import java.util.List; 49 import java.util.Map; 50 51 /** 52 * The PreferenceActivity class for managing sip profile preferences. 53 */ 54 public class SipSettings extends PreferenceActivity { 55 public static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; 56 57 static final String KEY_SIP_PROFILE = "sip_profile"; 58 static final int REQUEST_ADD_OR_EDIT_SIP_PROFILE = 1; 59 60 private static final String PREFIX = "[SipSettings] "; 61 private static final boolean VERBOSE = false; /* STOP SHIP if true */ 62 private static final int MENU_ADD_ACCOUNT = Menu.FIRST; 63 private static final String PREF_SIP_LIST = "sip_account_list"; 64 65 private PackageManager mPackageManager; 66 private SipManager mSipManager; 67 private SipProfileDb mProfileDb; 68 private SipProfile mProfile; // profile that's being edited 69 private PreferenceCategory mSipListContainer; 70 private Map<String, SipPreference> mSipPreferenceMap; 71 private List<SipProfile> mSipProfileList; 72 private SipPreferences mSipPreferences; 73 private int mUid = Process.myUid(); 74 75 private class SipPreference extends Preference { 76 SipProfile mProfile; SipPreference(Context c, SipProfile p)77 SipPreference(Context c, SipProfile p) { 78 super(c); 79 setProfile(p); 80 } 81 getProfile()82 SipProfile getProfile() { 83 return mProfile; 84 } 85 setProfile(SipProfile p)86 void setProfile(SipProfile p) { 87 mProfile = p; 88 setTitle(getProfileName(p)); 89 updateSummary(mSipPreferences.isReceivingCallsEnabled() 90 ? getString(R.string.registration_status_checking_status) 91 : getString(R.string.registration_status_not_receiving)); 92 } 93 updateSummary(String registrationStatus)94 void updateSummary(String registrationStatus) { 95 int profileUid = mProfile.getCallingUid(); 96 if (VERBOSE) { 97 log("SipPreference.updateSummary, profile uid: " + profileUid + 98 " registration: " + registrationStatus + 99 " status: " + registrationStatus); 100 } 101 String summary = ""; 102 if ((profileUid > 0) && (profileUid != mUid)) { 103 // from third party apps 104 summary = getString(R.string.third_party_account_summary, 105 getPackageNameFromUid(profileUid)); 106 } else { 107 summary = registrationStatus; 108 } 109 setSummary(summary); 110 } 111 } 112 getPackageNameFromUid(int uid)113 private String getPackageNameFromUid(int uid) { 114 try { 115 String[] pkgs = mPackageManager.getPackagesForUid(uid); 116 ApplicationInfo ai = mPackageManager.getApplicationInfo(pkgs[0], 0); 117 return ai.loadLabel(mPackageManager).toString(); 118 } catch (PackageManager.NameNotFoundException e) { 119 log("getPackageNameFromUid, cannot find name of uid: " + uid + ", exception: " + e); 120 } 121 return "uid:" + uid; 122 } 123 124 @Override onCreate(Bundle savedInstanceState)125 public void onCreate(Bundle savedInstanceState) { 126 super.onCreate(savedInstanceState); 127 128 mSipManager = SipManager.newInstance(this); 129 mSipPreferences = new SipPreferences(this); 130 mProfileDb = new SipProfileDb(this); 131 132 mPackageManager = getPackageManager(); 133 setContentView(R.layout.sip_settings_ui); 134 addPreferencesFromResource(R.xml.sip_setting); 135 mSipListContainer = (PreferenceCategory) findPreference(PREF_SIP_LIST); 136 137 ActionBar actionBar = getActionBar(); 138 if (actionBar != null) { 139 actionBar.setDisplayHomeAsUpEnabled(true); 140 } 141 } 142 143 @Override onResume()144 public void onResume() { 145 super.onResume(); 146 updateProfilesStatus(); 147 } 148 149 @Override onDestroy()150 protected void onDestroy() { 151 super.onDestroy(); 152 unregisterForContextMenu(getListView()); 153 } 154 155 @Override onActivityResult(final int requestCode, final int resultCode, final Intent intent)156 protected void onActivityResult(final int requestCode, final int resultCode, 157 final Intent intent) { 158 if (resultCode != RESULT_OK && resultCode != RESULT_FIRST_USER) return; 159 new Thread() { 160 @Override 161 public void run() { 162 try { 163 if (mProfile != null) { 164 if (VERBOSE) log("onActivityResult, remove: " + mProfile.getProfileName()); 165 deleteProfile(mProfile); 166 } 167 168 SipProfile profile = intent.getParcelableExtra(KEY_SIP_PROFILE); 169 if (resultCode == RESULT_OK) { 170 if (VERBOSE) log("onActivityResult, new: " + profile.getProfileName()); 171 addProfile(profile); 172 } 173 updateProfilesStatus(); 174 } catch (IOException e) { 175 log("onActivityResult, can not handle the profile: " + e); 176 } 177 } 178 }.start(); 179 } 180 updateProfilesStatus()181 private void updateProfilesStatus() { 182 new Thread(new Runnable() { 183 @Override 184 public void run() { 185 try { 186 retrieveSipLists(); 187 } catch (Exception e) { 188 log("updateProfilesStatus, exception: " + e); 189 } 190 } 191 }).start(); 192 } 193 getProfileName(SipProfile profile)194 private String getProfileName(SipProfile profile) { 195 String profileName = profile.getProfileName(); 196 if (TextUtils.isEmpty(profileName)) { 197 profileName = profile.getUserName() + "@" + profile.getSipDomain(); 198 } 199 return profileName; 200 } 201 retrieveSipLists()202 private void retrieveSipLists() { 203 mSipPreferenceMap = new LinkedHashMap<String, SipPreference>(); 204 mSipProfileList = mProfileDb.retrieveSipProfileList(); 205 processActiveProfilesFromSipService(); 206 Collections.sort(mSipProfileList, new Comparator<SipProfile>() { 207 @Override 208 public int compare(SipProfile p1, SipProfile p2) { 209 return getProfileName(p1).compareTo(getProfileName(p2)); 210 } 211 212 public boolean equals(SipProfile p) { 213 // not used 214 return false; 215 } 216 }); 217 mSipListContainer.removeAll(); 218 if (mSipProfileList.isEmpty()) { 219 getPreferenceScreen().removePreference(mSipListContainer); 220 } else { 221 getPreferenceScreen().addPreference(mSipListContainer); 222 for (SipProfile p : mSipProfileList) { 223 addPreferenceFor(p); 224 } 225 } 226 227 if (!mSipPreferences.isReceivingCallsEnabled()) return; 228 for (SipProfile p : mSipProfileList) { 229 if (mUid == p.getCallingUid()) { 230 try { 231 mSipManager.setRegistrationListener( 232 p.getUriString(), createRegistrationListener()); 233 } catch (SipException e) { 234 log("retrieveSipLists, cannot set registration listener: " + e); 235 } 236 } 237 } 238 } 239 processActiveProfilesFromSipService()240 private void processActiveProfilesFromSipService() { 241 SipProfile[] activeList = {}; 242 try { 243 activeList = mSipManager.getListOfProfiles(); 244 } catch (SipException e) { 245 log("SipManager could not retrieve SIP profiles: " + e); 246 } 247 for (SipProfile activeProfile : activeList) { 248 SipProfile profile = getProfileFromList(activeProfile); 249 if (profile == null) { 250 mSipProfileList.add(activeProfile); 251 } else { 252 profile.setCallingUid(activeProfile.getCallingUid()); 253 } 254 } 255 } 256 getProfileFromList(SipProfile activeProfile)257 private SipProfile getProfileFromList(SipProfile activeProfile) { 258 for (SipProfile p : mSipProfileList) { 259 if (p.getUriString().equals(activeProfile.getUriString())) { 260 return p; 261 } 262 } 263 return null; 264 } 265 addPreferenceFor(SipProfile p)266 private void addPreferenceFor(SipProfile p) { 267 String status; 268 if (VERBOSE) log("addPreferenceFor, profile uri: " + p.getUri()); 269 SipPreference pref = new SipPreference(this, p); 270 mSipPreferenceMap.put(p.getUriString(), pref); 271 mSipListContainer.addPreference(pref); 272 273 pref.setOnPreferenceClickListener( 274 new Preference.OnPreferenceClickListener() { 275 @Override 276 public boolean onPreferenceClick(Preference pref) { 277 handleProfileClick(((SipPreference) pref).mProfile); 278 return true; 279 } 280 }); 281 } 282 handleProfileClick(final SipProfile profile)283 private void handleProfileClick(final SipProfile profile) { 284 int uid = profile.getCallingUid(); 285 if (uid == mUid || uid == 0) { 286 startSipEditor(profile); 287 return; 288 } 289 new AlertDialog.Builder(this) 290 .setTitle(R.string.alert_dialog_close) 291 .setIconAttribute(android.R.attr.alertDialogIcon) 292 .setPositiveButton(R.string.close_profile, 293 new DialogInterface.OnClickListener() { 294 @Override 295 public void onClick(DialogInterface dialog, int w) { 296 deleteProfile(profile); 297 unregisterProfile(profile); 298 } 299 }) 300 .setNegativeButton(android.R.string.cancel, null) 301 .show(); 302 } 303 unregisterProfile(final SipProfile p)304 private void unregisterProfile(final SipProfile p) { 305 // run it on background thread for better UI response 306 new Thread(new Runnable() { 307 @Override 308 public void run() { 309 try { 310 mSipManager.close(p.getUriString()); 311 } catch (Exception e) { 312 log("unregisterProfile, unregister failed, SipService died? Exception: " + e); 313 } 314 } 315 }, "unregisterProfile").start(); 316 } 317 deleteProfile(SipProfile p)318 void deleteProfile(SipProfile p) { 319 mSipProfileList.remove(p); 320 SipPreference pref = mSipPreferenceMap.remove(p.getUriString()); 321 if (pref != null) { 322 mSipListContainer.removePreference(pref); 323 } 324 } 325 addProfile(SipProfile p)326 private void addProfile(SipProfile p) throws IOException { 327 try { 328 mSipManager.setRegistrationListener(p.getUriString(), 329 createRegistrationListener()); 330 } catch (Exception e) { 331 log("addProfile, cannot set registration listener: " + e); 332 } 333 mSipProfileList.add(p); 334 addPreferenceFor(p); 335 } 336 startSipEditor(final SipProfile profile)337 private void startSipEditor(final SipProfile profile) { 338 mProfile = profile; 339 Intent intent = new Intent(this, SipEditor.class); 340 intent.putExtra(KEY_SIP_PROFILE, (Parcelable) profile); 341 startActivityForResult(intent, REQUEST_ADD_OR_EDIT_SIP_PROFILE); 342 } 343 showRegistrationMessage(final String profileUri, final String message)344 private void showRegistrationMessage(final String profileUri, 345 final String message) { 346 runOnUiThread(new Runnable() { 347 @Override 348 public void run() { 349 SipPreference pref = mSipPreferenceMap.get(profileUri); 350 if (pref != null) { 351 pref.updateSummary(message); 352 } 353 } 354 }); 355 } 356 createRegistrationListener()357 private SipRegistrationListener createRegistrationListener() { 358 return new SipRegistrationListener() { 359 @Override 360 public void onRegistrationDone(String profileUri, long expiryTime) { 361 showRegistrationMessage(profileUri, getString( 362 R.string.registration_status_done)); 363 } 364 365 @Override 366 public void onRegistering(String profileUri) { 367 showRegistrationMessage(profileUri, getString( 368 R.string.registration_status_registering)); 369 } 370 371 @Override 372 public void onRegistrationFailed(String profileUri, int errorCode, 373 String message) { 374 switch (errorCode) { 375 case SipErrorCode.IN_PROGRESS: 376 showRegistrationMessage(profileUri, getString( 377 R.string.registration_status_still_trying)); 378 break; 379 case SipErrorCode.INVALID_CREDENTIALS: 380 showRegistrationMessage(profileUri, getString( 381 R.string.registration_status_invalid_credentials)); 382 break; 383 case SipErrorCode.SERVER_UNREACHABLE: 384 showRegistrationMessage(profileUri, getString( 385 R.string.registration_status_server_unreachable)); 386 break; 387 case SipErrorCode.DATA_CONNECTION_LOST: 388 if (SipManager.isSipWifiOnly(getApplicationContext())){ 389 showRegistrationMessage(profileUri, getString( 390 R.string.registration_status_no_wifi_data)); 391 } else { 392 showRegistrationMessage(profileUri, getString( 393 R.string.registration_status_no_data)); 394 } 395 break; 396 case SipErrorCode.CLIENT_ERROR: 397 showRegistrationMessage(profileUri, getString( 398 R.string.registration_status_not_running)); 399 break; 400 default: 401 showRegistrationMessage(profileUri, getString( 402 R.string.registration_status_failed_try_later, 403 message)); 404 } 405 } 406 }; 407 } 408 409 @Override 410 public boolean onCreateOptionsMenu(Menu menu) { 411 super.onCreateOptionsMenu(menu); 412 MenuItem addAccountMenuItem = menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_sip_account); 413 addAccountMenuItem.setIcon(R.drawable.ic_add_gnu_grey); 414 addAccountMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 415 return true; 416 } 417 418 @Override 419 public boolean onPrepareOptionsMenu(Menu menu) { 420 menu.findItem(MENU_ADD_ACCOUNT).setEnabled(SipUtil.isPhoneIdle(this)); 421 return super.onPrepareOptionsMenu(menu); 422 } 423 424 @Override 425 public boolean onOptionsItemSelected(MenuItem item) { 426 final int itemId = item.getItemId(); 427 switch (itemId) { 428 case MENU_ADD_ACCOUNT: { 429 startSipEditor(null); 430 return true; 431 } 432 case android.R.id.home: { 433 onBackPressed(); 434 return true; 435 } 436 } 437 return super.onOptionsItemSelected(item); 438 } 439 440 private static void log(String msg) { 441 Log.d(SipUtil.LOG_TAG, PREFIX + msg); 442 } 443 } 444