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