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.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 
     49     private SipUtil() {
     50     }
     51 
     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 
     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 
     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      */
     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      */
     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      */
    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      */
    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                 dbDeStorage.deleteProfile(profileToMove);
    168             }
    169         }
    170         // Delete supporting structures if they exist
    171         dbDeStorage.cleanupUponMigration();
    172     }
    173 
    174     /**
    175      * Migrates the DB files over from CE->DE storage and starts the SipService.
    176      */
    177     public static void startSipService() {
    178         Context phoneGlobalsContext = PhoneGlobals.getInstance();
    179         // Migrate SIP database from DE->CE storage if the device has just upgraded.
    180         possiblyMigrateSipDb(phoneGlobalsContext);
    181         // Wait until boot complete to start SIP so that it has access to CE storage.
    182         SipService.start(phoneGlobalsContext);
    183     }
    184 
    185     /**
    186      * Determines if the user has chosen to use SIP for PSTN calls as well as SIP calls.
    187      * @param context The context.
    188      * @return {@code True} if SIP should be used for PSTN calls.
    189      */
    190     private static boolean useSipForPstnCalls(Context context) {
    191         final SipPreferences sipPreferences = new SipPreferences(context);
    192         return sipPreferences.getSipCallOption().equals(Settings.System.SIP_ALWAYS);
    193     }
    194 
    195     /**
    196      * Updates SIP accounts to indicate whether they are enabled to receive incoming SIP calls.
    197      *
    198      * @param isEnabled {@code True} if receiving incoming SIP calls.
    199      */
    200     public static void useSipToReceiveIncomingCalls(Context context, boolean isEnabled) {
    201         SipProfileDb profileDb = new SipProfileDb(context);
    202 
    203         // Mark all profiles as auto-register if we are now receiving calls.
    204         List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
    205         for (SipProfile p : sipProfileList) {
    206             updateAutoRegistrationFlag(p, profileDb, isEnabled);
    207         }
    208     }
    209 
    210     private static void updateAutoRegistrationFlag(
    211             SipProfile p, SipProfileDb db, boolean isEnabled) {
    212         SipProfile newProfile = new SipProfile.Builder(p).setAutoRegistration(isEnabled).build();
    213 
    214         try {
    215             // Note: The profile is updated, but the associated PhoneAccount is left alone since
    216             // the only thing that changed is the auto-registration flag, which is not part of the
    217             // PhoneAccount.
    218             db.deleteProfile(p);
    219             db.saveProfile(newProfile);
    220         } catch (Exception e) {
    221             Log.d(LOG_TAG, "updateAutoRegistrationFlag, exception: " + e);
    222         }
    223     }
    224 }
    225