| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.services.telephony.sip; |
| |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.drawable.Icon; |
| import android.net.Uri; |
| import android.net.sip.SipManager; |
| import android.net.sip.SipProfile; |
| import android.provider.Settings; |
| import android.telecom.PhoneAccount; |
| import android.telecom.PhoneAccountHandle; |
| import android.telecom.TelecomManager; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.phone.PhoneGlobals; |
| import com.android.phone.R; |
| import com.android.server.sip.SipService; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class SipUtil { |
| static final String LOG_TAG = "SIP"; |
| static final String EXTRA_INCOMING_CALL_INTENT = |
| "com.android.services.telephony.sip.incoming_call_intent"; |
| static final String EXTRA_PHONE_ACCOUNT = |
| "com.android.services.telephony.sip.phone_account"; |
| |
| private SipUtil() { |
| } |
| |
| public static boolean isVoipSupported(Context context) { |
| return SipManager.isVoipSupported(context) && |
| context.getResources().getBoolean( |
| com.android.internal.R.bool.config_built_in_sip_phone) && |
| context.getResources().getBoolean( |
| com.android.internal.R.bool.config_voice_capable); |
| } |
| |
| static PendingIntent createIncomingCallPendingIntent( |
| Context context, String sipProfileName) { |
| Intent intent = new Intent(context, SipBroadcastReceiver.class); |
| intent.setAction(SipManager.ACTION_SIP_INCOMING_CALL); |
| intent.putExtra(EXTRA_PHONE_ACCOUNT, SipUtil.createAccountHandle(context, sipProfileName)); |
| return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); |
| } |
| |
| public static boolean isPhoneIdle(Context context) { |
| TelecomManager manager = (TelecomManager) context.getSystemService( |
| Context.TELECOM_SERVICE); |
| if (manager != null) { |
| return !manager.isInCall(); |
| } |
| return true; |
| } |
| |
| /** |
| * Creates a {@link PhoneAccountHandle} from the specified SIP profile name. |
| */ |
| static PhoneAccountHandle createAccountHandle(Context context, String sipProfileName) { |
| return new PhoneAccountHandle( |
| new ComponentName(context, SipConnectionService.class), sipProfileName); |
| } |
| |
| /** |
| * Determines the SIP profile name for a specified {@link PhoneAccountHandle}. |
| * |
| * @param phoneAccountHandle The {@link PhoneAccountHandle}. |
| * @return The SIP profile name. |
| */ |
| static String getSipProfileNameFromPhoneAccount(PhoneAccountHandle phoneAccountHandle) { |
| if (phoneAccountHandle == null) { |
| return null; |
| } |
| |
| String sipProfileName = phoneAccountHandle.getId(); |
| if (TextUtils.isEmpty(sipProfileName)) { |
| return null; |
| } |
| return sipProfileName; |
| } |
| |
| /** |
| * Creates a PhoneAccount for a SipProfile. |
| * |
| * @param context The context |
| * @param profile The SipProfile. |
| * @return The PhoneAccount. |
| */ |
| static PhoneAccount createPhoneAccount(Context context, SipProfile profile) { |
| // Build a URI to represent the SIP account. Does not use SipProfile#getUriString() since |
| // that prototype can include transport information which we do not want to see in the |
| // phone account. |
| String sipAddress = profile.getUserName() + "@" + profile.getSipDomain(); |
| Uri sipUri = Uri.parse(profile.getUriString()); |
| |
| PhoneAccountHandle accountHandle = |
| SipUtil.createAccountHandle(context, profile.getProfileName()); |
| |
| final ArrayList<String> supportedUriSchemes = new ArrayList<String>(); |
| supportedUriSchemes.add(PhoneAccount.SCHEME_SIP); |
| if (useSipForPstnCalls(context)) { |
| supportedUriSchemes.add(PhoneAccount.SCHEME_TEL); |
| } |
| |
| PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, profile.getDisplayName()) |
| .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
| | PhoneAccount.CAPABILITY_MULTI_USER) |
| .setAddress(sipUri) |
| .setShortDescription(sipAddress) |
| .setIcon(Icon.createWithResource( |
| context.getResources(), R.drawable.ic_dialer_sip_black_24dp)) |
| .setSupportedUriSchemes(supportedUriSchemes); |
| |
| return builder.build(); |
| } |
| |
| /** |
| * Upon migration from M->N, the SIP Profile database will be moved into DE storage. This will |
| * not be a problem for non-FBE enabled devices, since DE and CE storage is available at the |
| * same time. This will be a problem for backup/restore, however if the SIP Profile DB is |
| * restored onto a new FBE enabled device. |
| * |
| * Checks if the Sip Db is in DE storage. If it is, the Db is moved to CE storage and |
| * deleted. |
| */ |
| private static void possiblyMigrateSipDb(Context context) { |
| SipProfileDb dbDeStorage = new SipProfileDb(context); |
| dbDeStorage.accessDEStorageForMigration(); |
| List<SipProfile> profilesDeStorage = dbDeStorage.retrieveSipProfileList(); |
| if(profilesDeStorage.size() != 0) { |
| Log.i(LOG_TAG, "Migrating SIP Profiles over!"); |
| SipProfileDb dbCeStorage = new SipProfileDb(context); |
| //Perform Profile Migration |
| for (SipProfile profileToMove : profilesDeStorage) { |
| if (dbCeStorage.retrieveSipProfileFromName( |
| profileToMove.getProfileName()) == null) { |
| try { |
| dbCeStorage.saveProfile(profileToMove); |
| } catch (IOException e) { |
| Log.w(LOG_TAG, "Error Migrating file to CE: " + |
| profileToMove.getProfileName(), e); |
| } |
| } |
| Log.i(LOG_TAG, "(Migration) Deleting SIP profile: " + |
| profileToMove.getProfileName()); |
| try { |
| dbDeStorage.deleteProfile(profileToMove); |
| } catch (IOException e) { |
| Log.w(LOG_TAG, "Error Deleting file: " + |
| profileToMove.getProfileName(), e); |
| } |
| } |
| } |
| // Delete supporting structures if they exist |
| dbDeStorage.cleanupUponMigration(); |
| } |
| |
| /** |
| * Migrates the DB files over from CE->DE storage and starts the SipService. |
| */ |
| public static void startSipService() { |
| Context phoneGlobalsContext = PhoneGlobals.getInstance(); |
| // Migrate SIP database from DE->CE storage if the device has just upgraded. |
| possiblyMigrateSipDb(phoneGlobalsContext); |
| // Wait until boot complete to start SIP so that it has access to CE storage. |
| SipService.start(phoneGlobalsContext); |
| } |
| |
| /** |
| * Determines if the user has chosen to use SIP for PSTN calls as well as SIP calls. |
| * @param context The context. |
| * @return {@code True} if SIP should be used for PSTN calls. |
| */ |
| private static boolean useSipForPstnCalls(Context context) { |
| final SipPreferences sipPreferences = new SipPreferences(context); |
| return sipPreferences.getSipCallOption().equals(Settings.System.SIP_ALWAYS); |
| } |
| |
| /** |
| * Updates SIP accounts to indicate whether they are enabled to receive incoming SIP calls. |
| * |
| * @param isEnabled {@code True} if receiving incoming SIP calls. |
| */ |
| public static void useSipToReceiveIncomingCalls(Context context, boolean isEnabled) { |
| SipProfileDb profileDb = new SipProfileDb(context); |
| |
| // Mark all profiles as auto-register if we are now receiving calls. |
| List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList(); |
| for (SipProfile p : sipProfileList) { |
| updateAutoRegistrationFlag(p, profileDb, isEnabled); |
| } |
| } |
| |
| private static void updateAutoRegistrationFlag( |
| SipProfile p, SipProfileDb db, boolean isEnabled) { |
| SipProfile newProfile = new SipProfile.Builder(p).setAutoRegistration(isEnabled).build(); |
| |
| try { |
| // Note: The profile is updated, but the associated PhoneAccount is left alone since |
| // the only thing that changed is the auto-registration flag, which is not part of the |
| // PhoneAccount. |
| db.deleteProfile(p); |
| db.saveProfile(newProfile); |
| } catch (Exception e) { |
| Log.d(LOG_TAG, "updateAutoRegistrationFlag, exception: " + e); |
| } |
| } |
| } |