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 
17 package androidx.appcompat.mms;
18 
19 import android.app.PendingIntent;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.telephony.SmsManager;
24 import android.util.SparseArray;
25 
26 /**
27  * The public interface of MMS library
28  */
29 public 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      */
setForceLegacyMms(boolean forceLegacyMms)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      */
setThreadPoolSize(int size)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      */
setUseWakeLock(final boolean useWakeLock)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      */
setCarrierConfigValuesLoader(CarrierConfigValuesLoader loader)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      */
setApnSettingsLoader(ApnSettingsLoader loader)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      */
setUserAgentInfoLoader(final UserAgentInfoLoader loader)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      */
sendMultimediaMessage(int subId, Context context, Uri contentUri, String locationUrl, PendingIntent sentIntent)136     public static void sendMultimediaMessage(int subId, Context context, Uri contentUri,
137             String locationUrl, PendingIntent sentIntent) {
138         if (shouldUseLegacyMms()) {
139             MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
140         } else {
141             subId = Utils.getEffectiveSubscriptionId(subId);
142             final SmsManager smsManager = Utils.getSmsManager(subId);
143             smsManager.sendMultimediaMessage(context, contentUri, locationUrl,
144                     getConfigOverrides(subId), 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      */
downloadMultimediaMessage(int subId, Context context, String locationUrl, Uri contentUri, PendingIntent downloadedIntent)158     public static void downloadMultimediaMessage(int subId, Context context, String locationUrl,
159             Uri contentUri, PendingIntent downloadedIntent) {
160         if (shouldUseLegacyMms()) {
161             MmsService.startRequest(context,
162                     new DownloadRequest(locationUrl, contentUri, downloadedIntent));
163         } else {
164             subId = Utils.getEffectiveSubscriptionId(subId);
165             final SmsManager smsManager = Utils.getSmsManager(subId);
166             smsManager.downloadMultimediaMessage(context, locationUrl, contentUri,
167                     getConfigOverrides(subId), downloadedIntent);
168         }
169     }
170 
171     /**
172      * Checks if we should use legacy APIs for MMS.
173      *
174      * @return true if forced to use legacy APIs or platform doesn't supports MMS APIs.
175      */
shouldUseLegacyMms()176     public static boolean shouldUseLegacyMms() {
177         return sForceLegacyMms || !Utils.hasMmsApi();
178     }
179 
180     /**
181      * Get carrier configuration values overrides when platform MMS API is called.
182      * We only need to compute this if customized carrier config values loader or
183      * user agent info loader are set
184      *
185      * @param subId the ID of the SIM to use
186      * @return a Bundle containing the overrides
187      */
getConfigOverrides(final int subId)188     private static Bundle getConfigOverrides(final int subId) {
189         if (!Utils.hasMmsApi()) {
190             // If MMS API is not present, it is not necessary to compute overrides
191             return null;
192         }
193         Bundle overrides = null;
194         synchronized (sConfigOverridesMap) {
195             overrides = sConfigOverridesMap.get(subId);
196             if (overrides == null) {
197                 overrides = new Bundle();
198                 sConfigOverridesMap.put(subId, overrides);
199                 computeOverridesLocked(subId, overrides);
200             }
201         }
202         return overrides;
203     }
204 
205     /**
206      * Compute the overrides, incorporating the user agent info
207      *
208      * @param subId the subId of the SIM to use
209      * @param overrides the computed values overrides
210      */
computeOverridesLocked(final int subId, final Bundle overrides)211     private static void computeOverridesLocked(final int subId, final Bundle overrides) {
212         // Overrides not computed yet
213         final CarrierConfigValuesLoader carrierConfigValuesLoader =
214                 MmsService.getCarrierConfigValuesLoader();
215         if (carrierConfigValuesLoader != null &&
216                 !(carrierConfigValuesLoader instanceof DefaultCarrierConfigValuesLoader)) {
217             // Compute the overrides for carrier config values first if the config loader
218             // is not the default one.
219             final Bundle systemValues = Utils.getSmsManager(subId).getCarrierConfigValues();
220             final Bundle callerValues =
221                     MmsService.getCarrierConfigValuesLoader().get(subId);
222             if (systemValues != null && callerValues != null) {
223                 computeConfigDelta(systemValues, callerValues, overrides);
224             } else if (systemValues == null && callerValues != null) {
225                 overrides.putAll(callerValues);
226             }
227         }
228         final UserAgentInfoLoader userAgentInfoLoader = MmsService.getUserAgentInfoLoader();
229         if (userAgentInfoLoader != null &&
230                 !(userAgentInfoLoader instanceof DefaultUserAgentInfoLoader)) {
231             // Also set the user agent and ua prof url via the overrides
232             // if the user agent loader is not the default one.
233             overrides.putString(UserAgentInfoLoader.CONFIG_USER_AGENT,
234                     userAgentInfoLoader.getUserAgent());
235             overrides.putString(UserAgentInfoLoader.CONFIG_UA_PROF_URL,
236                     userAgentInfoLoader.getUAProfUrl());
237         }
238     }
239 
240     /**
241      * Compute the delta between two sets of carrier configuration values: system and caller
242      *
243      * @param systemValues the system config values
244      * @param callerValues the caller's config values
245      * @param delta the delta of values (caller - system), using caller value to override system's
246      */
computeConfigDelta(final Bundle systemValues, final Bundle callerValues, final Bundle delta)247     private static void computeConfigDelta(final Bundle systemValues, final Bundle callerValues,
248             final Bundle delta) {
249         for (final String key : callerValues.keySet()) {
250             final Object callerValue = callerValues.get(key);
251             final Object systemValue = systemValues.get(key);
252             if ((callerValue != null && systemValue != null && !callerValue.equals(systemValue)) ||
253                     (callerValue != null && systemValue == null) ||
254                     (callerValue == null && systemValue != null)) {
255                 if (callerValue == null || callerValue instanceof String) {
256                     delta.putString(key, (String) callerValue);
257                 } else if (callerValue instanceof Integer) {
258                     delta.putInt(key, (Integer) callerValue);
259                 } else if (callerValue instanceof Boolean) {
260                     delta.putBoolean(key, (Boolean) callerValue);
261                 }
262             }
263         }
264     }
265 }
266