• 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.PendingIntent;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.graphics.drawable.Icon;
24 import android.net.Uri;
25 import android.net.sip.SipManager;
26 import android.net.sip.SipProfile;
27 import android.os.Bundle;
28 import android.provider.Settings;
29 import android.telecom.PhoneAccount;
30 import android.telecom.PhoneAccountHandle;
31 import android.telecom.TelecomManager;
32 import android.telephony.TelephonyManager;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 import com.android.phone.PhoneGlobals;
37 
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 public class SipUtil {
43     static final String LOG_TAG = "SIP";
44     static final String EXTRA_INCOMING_CALL_INTENT =
45             "com.android.services.telephony.sip.incoming_call_intent";
46     static final String EXTRA_PHONE_ACCOUNT =
47             "com.android.services.telephony.sip.phone_account";
48     static final String PHONE_PACKAGE = "com.android.phone";
49 
SipUtil()50     private SipUtil() {
51     }
52 
isVoipSupported(Context context)53     public static boolean isVoipSupported(Context context) {
54         return SipManager.isVoipSupported(context)
55                 && context.getResources().getBoolean(
56                         com.android.internal.R.bool.config_built_in_sip_phone)
57                         && ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
58                         .isVoiceCapable();
59     }
60 
createIncomingCallPendingIntent( Context context, String sipProfileName)61     static PendingIntent createIncomingCallPendingIntent(
62             Context context, String sipProfileName) {
63         Intent intent = new Intent(context, SipIncomingCallReceiver.class);
64         intent.setAction(SipManager.ACTION_SIP_INCOMING_CALL);
65         intent.putExtra(EXTRA_PHONE_ACCOUNT, SipUtil.createAccountHandle(context, sipProfileName));
66         return PendingIntent.getBroadcast(context, 0, intent,
67                 // Mutable because information associated with the call is passed back here.
68                 PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
69     }
70 
isPhoneIdle(Context context)71     public static boolean isPhoneIdle(Context context) {
72         TelecomManager manager = (TelecomManager) context.getSystemService(
73                 Context.TELECOM_SERVICE);
74         if (manager != null) {
75             return !manager.isInCall();
76         }
77         return true;
78     }
79 
80     /**
81      * Creates a {@link PhoneAccountHandle} from the specified SIP profile name.
82      */
createAccountHandle(Context context, String sipProfileName)83     static PhoneAccountHandle createAccountHandle(Context context, String sipProfileName) {
84         return new PhoneAccountHandle(
85                 new ComponentName(context, SipConnectionService.class), sipProfileName);
86     }
87 
88     /**
89      * Determines the SIP profile name for a specified {@link PhoneAccountHandle}.
90      *
91      * @param phoneAccountHandle The {@link PhoneAccountHandle}.
92      * @return The SIP profile name.
93      */
getSipProfileNameFromPhoneAccount(PhoneAccountHandle phoneAccountHandle)94     static String getSipProfileNameFromPhoneAccount(PhoneAccountHandle phoneAccountHandle) {
95         if (phoneAccountHandle == null) {
96             return null;
97         }
98 
99         String sipProfileName = phoneAccountHandle.getId();
100         if (TextUtils.isEmpty(sipProfileName)) {
101             return null;
102         }
103         return sipProfileName;
104     }
105 
106     /**
107      * Creates a PhoneAccount for a SipProfile.
108      *
109      * @param context The context
110      * @param profile The SipProfile.
111      * @return The PhoneAccount.
112      */
createPhoneAccount(Context context, SipProfile profile)113     static PhoneAccount createPhoneAccount(Context context, SipProfile profile) {
114         // Build a URI to represent the SIP account.  Does not use SipProfile#getUriString() since
115         // that prototype can include transport information which we do not want to see in the
116         // phone account.
117         String sipAddress = profile.getUserName() + "@" + profile.getSipDomain();
118         Uri sipUri = Uri.parse(profile.getUriString());
119 
120         PhoneAccountHandle accountHandle =
121                 SipUtil.createAccountHandle(context, profile.getProfileName());
122 
123         final ArrayList<String> supportedUriSchemes = new ArrayList<String>();
124         supportedUriSchemes.add(PhoneAccount.SCHEME_SIP);
125         if (useSipForPstnCalls(context)) {
126             supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
127         }
128 
129         Bundle phoneAccountExtras = new Bundle();
130         phoneAccountExtras.putBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE,
131                 true);
132 
133         PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, profile.getDisplayName())
134                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER
135                         | PhoneAccount.CAPABILITY_MULTI_USER)
136                 .setAddress(sipUri)
137                 .setShortDescription(sipAddress)
138                 .setIcon(Icon.createWithResource(
139                         context, R.drawable.ic_dialer_sip_black_24dp))
140                 .setExtras(phoneAccountExtras)
141                 .setSupportedUriSchemes(supportedUriSchemes);
142 
143         return builder.build();
144     }
145 
146     /**
147      * Upon migration from M->N, the SIP Profile database will be moved into DE storage. This will
148      * not be a problem for non-FBE enabled devices, since DE and CE storage is available at the
149      * same time. This will be a problem for backup/restore, however if the SIP Profile DB is
150      * restored onto a new FBE enabled device.
151      *
152      * Checks if the Sip Db is in DE storage. If it is, the Db is moved to CE storage and
153      * deleted.
154      */
possiblyMigrateSipDb(Context context)155     private static void possiblyMigrateSipDb(Context context) {
156         SipProfileDb dbDeStorage = new SipProfileDb(context);
157         dbDeStorage.accessDEStorageForMigration();
158         List<SipProfile> profilesDeStorage = dbDeStorage.retrieveSipProfileList();
159         if(profilesDeStorage.size() != 0) {
160             Log.i(LOG_TAG, "Migrating SIP Profiles over!");
161             SipProfileDb dbCeStorage = new SipProfileDb(context);
162             //Perform Profile Migration
163             for (SipProfile profileToMove : profilesDeStorage) {
164                 if (dbCeStorage.retrieveSipProfileFromName(
165                         profileToMove.getProfileName()) == null) {
166                     try {
167                         dbCeStorage.saveProfile(profileToMove);
168                     } catch (IOException e) {
169                         Log.w(LOG_TAG, "Error Migrating file to CE: " +
170                                 profileToMove.getProfileName(), e);
171                     }
172                 }
173                 Log.i(LOG_TAG, "(Migration) Deleting SIP profile: " +
174                         profileToMove.getProfileName());
175                 try {
176                     dbDeStorage.deleteProfile(profileToMove);
177                 } catch (IOException e) {
178                     Log.w(LOG_TAG, "Error Deleting file: " +
179                             profileToMove.getProfileName(), e);
180                 }
181             }
182         }
183         // Delete supporting structures if they exist
184         dbDeStorage.cleanupUponMigration();
185     }
186 
187     /**
188      * Migrates the DB files over from CE->DE storage and starts the SipService.
189      */
startSipService()190     public static void startSipService() {
191         Context phoneGlobalsContext = PhoneGlobals.getInstance();
192         // Migrate SIP database from DE->CE storage if the device has just upgraded.
193         possiblyMigrateSipDb(phoneGlobalsContext);
194         // Wait until boot complete to start SIP so that it has access to CE storage.
195         Intent startSipIntent = new Intent();
196         startSipIntent.setAction(SipManager.ACTION_START_SIP);
197         startSipIntent.setPackage(PHONE_PACKAGE);
198         phoneGlobalsContext.startService(startSipIntent);
199     }
200 
201     /**
202      * Determines if the user has chosen to use SIP for PSTN calls as well as SIP calls.
203      * @param context The context.
204      * @return {@code True} if SIP should be used for PSTN calls.
205      */
useSipForPstnCalls(Context context)206     private static boolean useSipForPstnCalls(Context context) {
207         final SipPreferences sipPreferences = new SipPreferences(context);
208         return sipPreferences.getSipCallOption().equals(Settings.System.SIP_ALWAYS);
209     }
210 
211     /**
212      * Updates SIP accounts to indicate whether they are enabled to receive incoming SIP calls.
213      *
214      * @param isEnabled {@code True} if receiving incoming SIP calls.
215      */
useSipToReceiveIncomingCalls(Context context, boolean isEnabled)216     public static void useSipToReceiveIncomingCalls(Context context, boolean isEnabled) {
217         SipProfileDb profileDb = new SipProfileDb(context);
218 
219         // Mark all profiles as auto-register if we are now receiving calls.
220         List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
221         for (SipProfile p : sipProfileList) {
222             updateAutoRegistrationFlag(p, profileDb, isEnabled);
223         }
224     }
225 
updateAutoRegistrationFlag( SipProfile p, SipProfileDb db, boolean isEnabled)226     private static void updateAutoRegistrationFlag(
227             SipProfile p, SipProfileDb db, boolean isEnabled) {
228         SipProfile newProfile = new SipProfile.Builder(p).setAutoRegistration(isEnabled).build();
229 
230         try {
231             // Note: The profile is updated, but the associated PhoneAccount is left alone since
232             // the only thing that changed is the auto-registration flag, which is not part of the
233             // PhoneAccount.
234             db.deleteProfile(p);
235             db.saveProfile(newProfile);
236         } catch (Exception e) {
237             Log.d(LOG_TAG, "updateAutoRegistrationFlag, exception: " + e);
238         }
239     }
240 }
241