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