1/*
2 * Copyright (C) 2015 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 android.support.v7.mms;
18
19import android.app.PendingIntent;
20import android.content.Context;
21import android.net.Uri;
22import android.os.Bundle;
23import android.telephony.SmsManager;
24import android.util.SparseArray;
25
26/**
27 * The public interface of MMS library
28 */
29public class MmsManager {
30    /**
31     * Default subscription ID
32     */
33    public static final int DEFAULT_SUB_ID = -1;
34
35    // Whether to force legacy MMS sending
36    private static volatile boolean sForceLegacyMms = false;
37
38    // Cached computed overrides for carrier configuration values
39    private static SparseArray<Bundle> sConfigOverridesMap = new SparseArray<>();
40
41    /**
42     * Set the flag about whether to force to use legacy system APIs instead of system MMS API
43     *
44     * @param forceLegacyMms value to set
45     */
46    public static void setForceLegacyMms(boolean forceLegacyMms) {
47        sForceLegacyMms = forceLegacyMms;
48    }
49
50    /**
51     * Set the size of thread pool for request execution.
52     *
53     * Default is 4
54     *
55     * Note: if system MMS API is used, this has no effect
56     *
57     * @param size thread pool size
58     */
59    public static void setThreadPoolSize(int size) {
60        MmsService.setThreadPoolSize(size);
61    }
62
63    /**
64     * Set whether to use wake lock while sending or downloading MMS.
65     *
66     * Default value is true
67     *
68     * Note: if system MMS API is used, this has no effect
69     *
70     * @param useWakeLock true to use wake lock, false otherwise
71     */
72    public static void setUseWakeLock(final boolean useWakeLock) {
73        MmsService.setUseWakeLock(useWakeLock);
74    }
75
76    /**
77     * Set the optional carrier config values loader
78     *
79     * Note: if system MMS API is used, this is used to compute the overrides
80     * of carrier configuration values
81     *
82     * @param loader the carrier config values loader
83     */
84    public static void setCarrierConfigValuesLoader(CarrierConfigValuesLoader loader) {
85        if (loader == null) {
86            throw new IllegalArgumentException("Carrier configuration loader can not be empty");
87        }
88        synchronized (sConfigOverridesMap) {
89            MmsService.setCarrierConfigValuesLoader(loader);
90            sConfigOverridesMap.clear();
91        }
92    }
93
94    /**
95     * Set the optional APN settings loader
96     *
97     * Note: if system MMS API is used, this has no effect
98     *
99     * @param loader the APN settings loader
100     */
101    public static void setApnSettingsLoader(ApnSettingsLoader loader) {
102        if (loader == null) {
103            throw new IllegalArgumentException("APN settings loader can not be empty");
104        }
105        MmsService.setApnSettingsLoader(loader);
106    }
107
108    /**
109     * Set user agent info loader
110     *
111     * Note: if system MMS API is used, this is used to compute the overrides
112     * of carrier configuration values
113
114     * @param loader the user agent info loader
115     */
116    public static void setUserAgentInfoLoader(final UserAgentInfoLoader loader) {
117        if (loader == null) {
118            throw new IllegalArgumentException("User agent info loader can not be empty");
119        }
120        synchronized (sConfigOverridesMap) {
121            MmsService.setUserAgentInfoLoader(loader);
122            sConfigOverridesMap.clear();
123        }
124    }
125
126    /**
127     * Send MMS via platform MMS API (if platform supports and not forced to
128     * use legacy APIs) or legacy APIs
129     *
130     * @param subId the subscription ID of the SIM to use
131     * @param context the Context to use
132     * @param contentUri the content URI of the PDU to be sent
133     * @param locationUrl the optional location URL to use for sending
134     * @param sentIntent the pending intent for returning results
135     */
136    public static void sendMultimediaMessage(int subId, Context context, Uri contentUri,
137            String locationUrl, PendingIntent sentIntent) {
138        if (Utils.hasMmsApi() && !sForceLegacyMms) {
139            subId = Utils.getEffectiveSubscriptionId(subId);
140            final SmsManager smsManager = Utils.getSmsManager(subId);
141            smsManager.sendMultimediaMessage(context, contentUri, locationUrl,
142                    getConfigOverrides(subId), sentIntent);
143        } else {
144            MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
145        }
146    }
147
148    /**
149     * Download MMS via platform MMS API (if platform supports and not forced to
150     * use legacy APIs) or legacy APIs
151     *
152     * @param subId the subscription ID of the SIM to use
153     * @param context the Context to use
154     * @param contentUri the content URI of the PDU to be sent
155     * @param locationUrl the optional location URL to use for sending
156     * @param downloadedIntent the pending intent for returning results
157     */
158    public static void downloadMultimediaMessage(int subId, Context context, String locationUrl,
159            Uri contentUri, PendingIntent downloadedIntent) {
160        if (Utils.hasMmsApi() && !sForceLegacyMms) {
161            subId = Utils.getEffectiveSubscriptionId(subId);
162            final SmsManager smsManager = Utils.getSmsManager(subId);
163            smsManager.downloadMultimediaMessage(context, locationUrl, contentUri,
164                    getConfigOverrides(subId), downloadedIntent);
165        } else {
166            MmsService.startRequest(context,
167                    new DownloadRequest(locationUrl, contentUri, downloadedIntent));
168        }
169    }
170
171    /**
172     * Get carrier configuration values overrides when platform MMS API is called.
173     * We only need to compute this if customized carrier config values loader or
174     * user agent info loader are set
175     *
176     * @param subId the ID of the SIM to use
177     * @return a Bundle containing the overrides
178     */
179    private static Bundle getConfigOverrides(final int subId) {
180        if (!Utils.hasMmsApi()) {
181            // If MMS API is not present, it is not necessary to compute overrides
182            return null;
183        }
184        Bundle overrides = null;
185        synchronized (sConfigOverridesMap) {
186            overrides = sConfigOverridesMap.get(subId);
187            if (overrides == null) {
188                overrides = new Bundle();
189                sConfigOverridesMap.put(subId, overrides);
190                computeOverridesLocked(subId, overrides);
191            }
192        }
193        return overrides;
194    }
195
196    /**
197     * Compute the overrides, incorporating the user agent info
198     *
199     * @param subId the subId of the SIM to use
200     * @param overrides the computed values overrides
201     */
202    private static void computeOverridesLocked(final int subId, final Bundle overrides) {
203        // Overrides not computed yet
204        final CarrierConfigValuesLoader carrierConfigValuesLoader =
205                MmsService.getCarrierConfigValuesLoader();
206        if (carrierConfigValuesLoader != null &&
207                !(carrierConfigValuesLoader instanceof DefaultCarrierConfigValuesLoader)) {
208            // Compute the overrides for carrier config values first if the config loader
209            // is not the default one.
210            final Bundle systemValues = Utils.getSmsManager(subId).getCarrierConfigValues();
211            final Bundle callerValues =
212                    MmsService.getCarrierConfigValuesLoader().get(subId);
213            if (systemValues != null && callerValues != null) {
214                computeConfigDelta(systemValues, callerValues, overrides);
215            } else if (systemValues == null && callerValues != null) {
216                overrides.putAll(callerValues);
217            }
218        }
219        final UserAgentInfoLoader userAgentInfoLoader = MmsService.getUserAgentInfoLoader();
220        if (userAgentInfoLoader != null &&
221                !(userAgentInfoLoader instanceof DefaultUserAgentInfoLoader)) {
222            // Also set the user agent and ua prof url via the overrides
223            // if the user agent loader is not the default one.
224            overrides.putString(UserAgentInfoLoader.CONFIG_USER_AGENT,
225                    userAgentInfoLoader.getUserAgent());
226            overrides.putString(UserAgentInfoLoader.CONFIG_UA_PROF_URL,
227                    userAgentInfoLoader.getUAProfUrl());
228        }
229    }
230
231    /**
232     * Compute the delta between two sets of carrier configuration values: system and caller
233     *
234     * @param systemValues the system config values
235     * @param callerValues the caller's config values
236     * @param delta the delta of values (caller - system), using caller value to override system's
237     */
238    private static void computeConfigDelta(final Bundle systemValues, final Bundle callerValues,
239            final Bundle delta) {
240        for (final String key : callerValues.keySet()) {
241            final Object callerValue = callerValues.get(key);
242            final Object systemValue = systemValues.get(key);
243            if ((callerValue != null && systemValue != null && !callerValue.equals(systemValue)) ||
244                    (callerValue != null && systemValue == null) ||
245                    (callerValue == null && systemValue != null)) {
246                if (callerValue == null || callerValue instanceof String) {
247                    delta.putString(key, (String) callerValue);
248                } else if (callerValue instanceof Integer) {
249                    delta.putInt(key, (Integer) callerValue);
250                } else if (callerValue instanceof Boolean) {
251                    delta.putBoolean(key, (Boolean) callerValue);
252                }
253            }
254        }
255    }
256}
257