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
17package com.android.phone;
18
19import android.content.Intent;
20import android.net.Uri;
21import android.telecom.PhoneAccount;
22import android.telephony.PhoneNumberUtils;
23import android.text.TextUtils;
24import android.util.Log;
25
26import com.android.internal.telephony.Connection;
27
28import 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 */
42public 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
75    public static synchronized CallGatewayManager getInstance() {
76        if (sSingleton == null) {
77            sSingleton = new CallGatewayManager();
78        }
79        return sSingleton;
80    }
81
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
202        public RawGatewayInfo(String packageName, Uri gatewayUri,
203                String trueNumber) {
204            this.packageName = packageName;
205            this.gatewayUri = gatewayUri;
206            this.trueNumber = trueNumber;
207        }
208
209        public String getFormattedGatewayNumber() {
210            return formatProviderUri(gatewayUri);
211        }
212
213        public boolean isEmpty() {
214            return TextUtils.isEmpty(packageName) || gatewayUri == null;
215        }
216    }
217}
218