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