• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.content.Context;
20 import android.net.sip.SipException;
21 import android.net.sip.SipManager;
22 import android.net.sip.SipProfile;
23 import android.telecom.PhoneAccount;
24 import android.telecom.PhoneAccountHandle;
25 import android.telecom.TelecomManager;
26 import android.util.Log;
27 
28 import java.util.List;
29 import java.util.Objects;
30 import java.util.concurrent.CopyOnWriteArrayList;
31 
32 /**
33  * Manages the {@link PhoneAccount} entries for SIP calling.
34  */
35 public final class SipAccountRegistry {
36     private final class AccountEntry {
37         private final SipProfile mProfile;
38 
AccountEntry(SipProfile profile)39         AccountEntry(SipProfile profile) {
40             mProfile = profile;
41         }
42 
getProfile()43         SipProfile getProfile() {
44             return mProfile;
45         }
46 
47         /**
48          * Starts the SIP service associated with the SIP profile.
49          *
50          * @param sipManager The SIP manager.
51          * @param context The context.
52          * @param isReceivingCalls {@code True} if the sip service is being started to make and
53          *          receive calls.  {@code False} if the sip service is being started only for
54          *          outgoing calls.
55          * @return {@code True} if the service started successfully.
56          */
startSipService(SipManager sipManager, Context context, boolean isReceivingCalls)57         boolean startSipService(SipManager sipManager, Context context, boolean isReceivingCalls) {
58             if (VERBOSE) log("startSipService, profile: " + mProfile);
59             try {
60                 // Stop the Sip service for the profile if it is already running.  This is important
61                 // if we are changing the state of the "receive calls" option.
62                 sipManager.close(mProfile.getUriString());
63 
64                 // Start the sip service for the profile.
65                 if (isReceivingCalls) {
66                     sipManager.open(
67                             mProfile,
68                             SipUtil.createIncomingCallPendingIntent(context,
69                                     mProfile.getProfileName()),
70                             null);
71                 } else {
72                     sipManager.open(mProfile);
73                 }
74                 return true;
75             } catch (SipException e) {
76                 log("startSipService, profile: " + mProfile.getProfileName() +
77                         ", exception: " + e);
78             }
79             return false;
80         }
81 
82         /**
83          * Stops the SIP service associated with the SIP profile.  The {@code SipAccountRegistry} is
84          * informed when the service has been stopped via an intent which triggers
85          * {@link SipAccountRegistry#removeSipProfile(String)}.
86          *
87          * @param sipManager The SIP manager.
88          * @return {@code True} if stop was successful.
89          */
stopSipService(SipManager sipManager)90         boolean stopSipService(SipManager sipManager) {
91             try {
92                 sipManager.close(mProfile.getUriString());
93                 return true;
94             } catch (Exception e) {
95                 log("stopSipService, stop failed for profile: " + mProfile.getUriString() +
96                         ", exception: " + e);
97             }
98             return false;
99         }
100     }
101 
102     private static final String PREFIX = "[SipAccountRegistry] ";
103     private static final boolean VERBOSE = false; /* STOP SHIP if true */
104     private static final SipAccountRegistry INSTANCE = new SipAccountRegistry();
105 
106     private final List<AccountEntry> mAccounts = new CopyOnWriteArrayList<>();
107 
SipAccountRegistry()108     private SipAccountRegistry() {}
109 
getInstance()110     public static SipAccountRegistry getInstance() {
111         return INSTANCE;
112     }
113 
114     /**
115      * Sets up the Account registry and performs any upgrade operations before it is used.
116      */
setup(Context context)117     public void setup(Context context) {
118         verifyAndPurgeInvalidPhoneAccounts(context);
119         startSipProfilesAsync(context, (String) null, false);
120     }
121 
122     /**
123      * Checks the existing SIP phone {@link PhoneAccount}s registered with telecom and deletes any
124      * invalid accounts.
125      *
126      * @param context The context.
127      */
verifyAndPurgeInvalidPhoneAccounts(Context context)128     void verifyAndPurgeInvalidPhoneAccounts(Context context) {
129         TelecomManager telecomManager = TelecomManager.from(context);
130         SipProfileDb profileDb = new SipProfileDb(context);
131         List<PhoneAccountHandle> accountHandles = telecomManager.getPhoneAccountsSupportingScheme(
132                 PhoneAccount.SCHEME_SIP);
133 
134         for (PhoneAccountHandle accountHandle : accountHandles) {
135             String profileName = SipUtil.getSipProfileNameFromPhoneAccount(accountHandle);
136             SipProfile profile = profileDb.retrieveSipProfileFromName(profileName);
137             if (profile == null) {
138                 log("verifyAndPurgeInvalidPhoneAccounts, deleting account: " + accountHandle);
139                 telecomManager.unregisterPhoneAccount(accountHandle);
140             }
141         }
142     }
143 
144     /**
145      * Starts the SIP service for the specified SIP profile and ensures it has a valid registered
146      * {@link PhoneAccount}.
147      *
148      * @param context The context.
149      * @param sipProfileName The name of the {@link SipProfile} to start, or {@code null} for all.
150      * @param enableProfile Sip account should be enabled
151      */
startSipService(Context context, String sipProfileName, boolean enableProfile)152     void startSipService(Context context, String sipProfileName, boolean enableProfile) {
153         startSipProfilesAsync(context, sipProfileName, enableProfile);
154     }
155 
156     /**
157      * Removes a {@link SipProfile} from the account registry.  Does not stop/close the associated
158      * SIP service (this method is invoked via an intent from the SipService once a profile has
159      * been stopped/closed).
160      *
161      * @param sipProfileName Name of the SIP profile.
162      */
removeSipProfile(String sipProfileName)163     public void removeSipProfile(String sipProfileName) {
164         AccountEntry accountEntry = getAccountEntry(sipProfileName);
165 
166         if (accountEntry != null) {
167             mAccounts.remove(accountEntry);
168         }
169     }
170 
171     /**
172      * Stops a SIP profile and un-registers its associated {@link android.telecom.PhoneAccount}.
173      * Called after a SIP profile is deleted.  The {@link AccountEntry} will be removed when the
174      * service has been stopped.  The {@code SipService} fires the {@code ACTION_SIP_REMOVE_PHONE}
175      * intent, which triggers {@link SipAccountRegistry#removeSipProfile(String)} to perform the
176      * removal.
177      *
178      * @param context The context.
179      * @param sipProfileName Name of the SIP profile.
180      */
stopSipService(Context context, String sipProfileName)181     void stopSipService(Context context, String sipProfileName) {
182         // Stop the sip service for the profile.
183         AccountEntry accountEntry = getAccountEntry(sipProfileName);
184         if (accountEntry != null ) {
185             SipManager sipManager = SipManager.newInstance(context);
186             accountEntry.stopSipService(sipManager);
187         }
188 
189         // Un-register its PhoneAccount.
190         PhoneAccountHandle handle = SipUtil.createAccountHandle(context, sipProfileName);
191         TelecomManager.from(context).unregisterPhoneAccount(handle);
192     }
193 
194     /**
195      * Causes the SIP service to be restarted for all {@link SipProfile}s.  For example, if the user
196      * toggles the "receive calls" option for SIP, this method handles restarting the SIP services
197      * in the new mode.
198      *
199      * @param context The context.
200      */
restartSipService(Context context)201     public void restartSipService(Context context) {
202         startSipProfiles(context, null, false);
203     }
204 
205     /**
206      * Performs an asynchronous call to
207      * {@link SipAccountRegistry#startSipProfiles(android.content.Context, String)}, starting the
208      * specified SIP profile and registering its {@link android.telecom.PhoneAccount}.
209      *
210      * @param context The context.
211      * @param sipProfileName Name of the SIP profile.
212      * @param enableProfile Sip account should be enabled.
213      */
startSipProfilesAsync( final Context context, final String sipProfileName, final boolean enableProfile)214     private void startSipProfilesAsync(
215             final Context context, final String sipProfileName, final boolean enableProfile) {
216         if (VERBOSE) log("startSipProfiles, start auto registration");
217 
218         new Thread(new Runnable() {
219             @Override
220             public void run() {
221                 startSipProfiles(context, sipProfileName, enableProfile);
222             }}
223         ).start();
224     }
225 
226     /**
227      * Loops through all SIP accounts from the SIP database, starts each service and registers
228      * each with the telecom framework. If a specific sipProfileName is specified, this will only
229      * register the associated SIP account.
230      *
231      * @param context The context.
232      * @param sipProfileName A specific SIP profile Name to start, or {@code null} to start all.
233      * @param enableProfile Sip account should be enabled.
234      */
startSipProfiles(Context context, String sipProfileName, boolean enableProfile)235     private void startSipProfiles(Context context, String sipProfileName, boolean enableProfile) {
236         final SipPreferences sipPreferences = new SipPreferences(context);
237         boolean isReceivingCalls = sipPreferences.isReceivingCallsEnabled();
238         TelecomManager telecomManager = TelecomManager.from(context);
239         SipManager sipManager = SipManager.newInstance(context);
240         SipProfileDb profileDb = new SipProfileDb(context);
241         List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
242 
243         for (SipProfile profile : sipProfileList) {
244             // Register a PhoneAccount for the profile and optionally enable the primary
245             // profile.
246             if (sipProfileName == null || sipProfileName.equals(profile.getProfileName())) {
247                 PhoneAccount phoneAccount = SipUtil.createPhoneAccount(context, profile);
248                 telecomManager.registerPhoneAccount(phoneAccount);
249                 if (enableProfile) {
250                     telecomManager.enablePhoneAccount(phoneAccount.getAccountHandle(), true);
251                 }
252                 startSipServiceForProfile(profile, sipManager, context, isReceivingCalls);
253             }
254         }
255     }
256 
257     /**
258      * Starts the SIP service for a sip profile and saves a new {@code AccountEntry} in the
259      * registry.
260      *
261      * @param profile The {@link SipProfile} to start.
262      * @param sipManager The SIP manager.
263      * @param context The context.
264      * @param isReceivingCalls {@code True} if the profile should be started such that it can
265      *      receive incoming calls.
266      */
startSipServiceForProfile(SipProfile profile, SipManager sipManager, Context context, boolean isReceivingCalls)267     private void startSipServiceForProfile(SipProfile profile, SipManager sipManager,
268             Context context, boolean isReceivingCalls) {
269         removeSipProfile(profile.getUriString());
270 
271         AccountEntry entry = new AccountEntry(profile);
272         if (entry.startSipService(sipManager, context, isReceivingCalls)) {
273             mAccounts.add(entry);
274         }
275     }
276 
277     /**
278      * Retrieves the {@link AccountEntry} from the registry with the specified name.
279      *
280      * @param sipProfileName Name of the SIP profile to retrieve.
281      * @return The {@link AccountEntry}, or {@code null} is it was not found.
282      */
getAccountEntry(String sipProfileName)283     private AccountEntry getAccountEntry(String sipProfileName) {
284         for (AccountEntry entry : mAccounts) {
285             if (Objects.equals(sipProfileName, entry.getProfile().getProfileName())) {
286                 return entry;
287             }
288         }
289         return null;
290     }
291 
log(String message)292     private void log(String message) {
293         Log.d(SipUtil.LOG_TAG, PREFIX + message);
294     }
295 }
296