• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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