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