1 /* 2 * Copyright (C) 2013 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.phone; 18 19 import android.content.Intent; 20 import android.net.Uri; 21 import android.telecom.PhoneAccount; 22 import android.telephony.PhoneNumberUtils; 23 import android.text.TextUtils; 24 import android.util.Log; 25 26 import com.android.internal.telephony.Connection; 27 28 import java.util.concurrent.ConcurrentHashMap; 29 30 /** 31 * This class manages gateway information for outgoing calls. When calls are made, they may contain 32 * gateway information for services which route phone calls through their own service/numbers. 33 * The data consists of a number to call and the package name of the service. This data is used in 34 * two ways:<br/> 35 * 1. Call the appropriate routing number<br/> 36 * 2. Display information about the routing to the user<br/> 37 * 38 * <p>When an outgoing call is finally placed in PhoneUtils.placeCall, it uses this class to get the 39 * proper number to dial. It also saves an association between the connection object and the gateway 40 * data into this class. 41 */ 42 public class CallGatewayManager { 43 private static final String LOG_TAG = CallGatewayManager.class.getSimpleName(); 44 45 /** 46 * Intent extra to specify the package name of the gateway 47 * provider. Used to get the name displayed in the in-call screen 48 * during the call setup. The value is a string. 49 */ 50 // TODO: This extra is currently set by the gateway application as 51 // a temporary measure. Ultimately, the framework will securely 52 // set it. 53 /* package */ static final String EXTRA_GATEWAY_PROVIDER_PACKAGE = 54 "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE"; 55 56 /** 57 * Intent extra to specify the URI of the provider to place the 58 * call. The value is a string. It holds the gateway address 59 * (phone gateway URL should start with the 'tel:' scheme) that 60 * will actually be contacted to call the number passed in the 61 * intent URL or in the EXTRA_PHONE_NUMBER extra. 62 */ 63 // TODO: Should the value be a Uri (Parcelable)? Need to make sure 64 // MMI code '#' don't get confused as URI fragments. 65 /* package */ static final String EXTRA_GATEWAY_URI = 66 "com.android.phone.extra.GATEWAY_URI"; 67 68 public static final RawGatewayInfo EMPTY_INFO = new RawGatewayInfo(null, null, null); 69 70 private final ConcurrentHashMap<Connection, RawGatewayInfo> mMap = 71 new ConcurrentHashMap<Connection, RawGatewayInfo>(4, 0.9f, 1); 72 73 private static CallGatewayManager sSingleton; 74 getInstance()75 public static synchronized CallGatewayManager getInstance() { 76 if (sSingleton == null) { 77 sSingleton = new CallGatewayManager(); 78 } 79 return sSingleton; 80 } 81 CallGatewayManager()82 private CallGatewayManager() { 83 } 84 85 /** 86 * Static method returns an object containing the gateway data stored in the extras of the 87 * Intent parameter. If no such data exists, returns a Null-Object RawGatewayInfo. 88 * @param intent The intent from which to read gateway data. 89 * @return A populated or empty RawGatewayInfo object. 90 */ getRawGatewayInfo(Intent intent, String number)91 public static RawGatewayInfo getRawGatewayInfo(Intent intent, String number) { 92 if (hasPhoneProviderExtras(intent)) { 93 return new RawGatewayInfo(intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE), 94 getProviderGatewayUri(intent), number); 95 } 96 return EMPTY_INFO; 97 } 98 99 /** 100 * This function sets the current mapping from connection to gatewayInfo. 101 * @param connection The connection object for the placed outgoing call. 102 * @param gatewayInfo Gateway info gathered using getRawGatewayInfo. 103 */ setGatewayInfoForConnection(Connection connection, RawGatewayInfo gatewayInfo)104 public void setGatewayInfoForConnection(Connection connection, RawGatewayInfo gatewayInfo) { 105 if (!gatewayInfo.isEmpty()) { 106 mMap.put(connection, gatewayInfo); 107 } else { 108 mMap.remove(connection); 109 } 110 } 111 112 /** 113 * Clears the gateway information previously stored via setGatewayInfoForConnection. 114 */ clearGatewayData(Connection connection)115 public void clearGatewayData(Connection connection) { 116 setGatewayInfoForConnection(connection, EMPTY_INFO); 117 } 118 119 /** 120 * If the parameter matches the connection object we previously saved through 121 * setGatewayInfoForConnection, return the associated raw gateway info data. If not, then 122 * return an empty raw gateway info. 123 */ getGatewayInfo(Connection connection)124 public RawGatewayInfo getGatewayInfo(Connection connection) { 125 final RawGatewayInfo info = mMap.get(connection); 126 if (info != null) { 127 return info; 128 } 129 130 return EMPTY_INFO; 131 } 132 133 /** 134 * Check if all the provider's info is present in the intent. 135 * @param intent Expected to have the provider's extra. 136 * @return true if the intent has all the extras to build the 137 * in-call screen's provider info overlay. 138 */ hasPhoneProviderExtras(Intent intent)139 public static boolean hasPhoneProviderExtras(Intent intent) { 140 if (null == intent) { 141 return false; 142 } 143 final String name = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE); 144 final String gatewayUri = intent.getStringExtra(EXTRA_GATEWAY_URI); 145 146 return !TextUtils.isEmpty(name) && !TextUtils.isEmpty(gatewayUri); 147 } 148 149 /** 150 * Copy all the expected extras set when a 3rd party provider is 151 * used from the source intent to the destination one. Checks all 152 * the required extras are present, if any is missing, none will 153 * be copied. 154 * @param src Intent which may contain the provider's extras. 155 * @param dst Intent where a copy of the extras will be added if applicable. 156 */ checkAndCopyPhoneProviderExtras(Intent src, Intent dst)157 public static void checkAndCopyPhoneProviderExtras(Intent src, Intent dst) { 158 if (!hasPhoneProviderExtras(src)) { 159 Log.d(LOG_TAG, "checkAndCopyPhoneProviderExtras: some or all extras are missing."); 160 return; 161 } 162 163 dst.putExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE, 164 src.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE)); 165 dst.putExtra(EXTRA_GATEWAY_URI, 166 src.getStringExtra(EXTRA_GATEWAY_URI)); 167 } 168 169 /** 170 * Return the gateway uri from the intent. 171 * @param intent With the gateway uri extra. 172 * @return The gateway URI or null if not found. 173 */ getProviderGatewayUri(Intent intent)174 public static Uri getProviderGatewayUri(Intent intent) { 175 final String uri = intent.getStringExtra(EXTRA_GATEWAY_URI); 176 return TextUtils.isEmpty(uri) ? null : Uri.parse(uri); 177 } 178 179 /** 180 * Return a formatted version of the uri's scheme specific 181 * part. E.g for 'tel:12345678', return '1-234-5678'. 182 * @param uri A 'tel:' URI with the gateway phone number. 183 * @return the provider's address (from the gateway uri) formatted 184 * for user display. null if uri was null or its scheme was not 'tel:'. 185 */ formatProviderUri(Uri uri)186 public static String formatProviderUri(Uri uri) { 187 if (uri != null) { 188 if (PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) { 189 return PhoneNumberUtils.formatNumber(uri.getSchemeSpecificPart()); 190 } else { 191 return uri.toString(); 192 } 193 } 194 return null; 195 } 196 197 public static class RawGatewayInfo { 198 public String packageName; 199 public Uri gatewayUri; 200 public String trueNumber; 201 RawGatewayInfo(String packageName, Uri gatewayUri, String trueNumber)202 public RawGatewayInfo(String packageName, Uri gatewayUri, 203 String trueNumber) { 204 this.packageName = packageName; 205 this.gatewayUri = gatewayUri; 206 this.trueNumber = trueNumber; 207 } 208 getFormattedGatewayNumber()209 public String getFormattedGatewayNumber() { 210 return formatProviderUri(gatewayUri); 211 } 212 isEmpty()213 public boolean isEmpty() { 214 return TextUtils.isEmpty(packageName) || gatewayUri == null; 215 } 216 } 217 } 218