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