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