1 /*
2  * Copyright (C) 2014 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.mms.service;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.database.sqlite.SqliteWrapper;
22 import android.net.NetworkUtils;
23 import android.net.Uri;
24 import android.provider.Telephony;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import com.android.internal.telephony.PhoneConstants;
29 import com.android.mms.service.exception.ApnException;
30 
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 
34 /**
35  * APN settings used for MMS transactions
36  */
37 public class ApnSettings {
38     private static final String TAG = MmsService.TAG;
39 
40     // MMSC URL
41     private final String mServiceCenter;
42     // MMSC proxy address
43     private final String mProxyAddress;
44     // MMSC proxy port
45     private final int mProxyPort;
46     // Debug text for this APN: a concatenation of interesting columns of this APN
47     private final String mDebugText;
48 
49     private static final String[] APN_PROJECTION = {
50             Telephony.Carriers.TYPE,
51             Telephony.Carriers.MMSC,
52             Telephony.Carriers.MMSPROXY,
53             Telephony.Carriers.MMSPORT,
54             Telephony.Carriers.NAME,
55             Telephony.Carriers.APN,
56             Telephony.Carriers.BEARER,
57             Telephony.Carriers.PROTOCOL,
58             Telephony.Carriers.ROAMING_PROTOCOL,
59             Telephony.Carriers.AUTH_TYPE,
60             Telephony.Carriers.MVNO_TYPE,
61             Telephony.Carriers.MVNO_MATCH_DATA,
62             Telephony.Carriers.PROXY,
63             Telephony.Carriers.PORT,
64             Telephony.Carriers.SERVER,
65             Telephony.Carriers.USER,
66             Telephony.Carriers.PASSWORD,
67     };
68     private static final int COLUMN_TYPE         = 0;
69     private static final int COLUMN_MMSC         = 1;
70     private static final int COLUMN_MMSPROXY     = 2;
71     private static final int COLUMN_MMSPORT      = 3;
72     private static final int COLUMN_NAME         = 4;
73     private static final int COLUMN_APN          = 5;
74     private static final int COLUMN_BEARER       = 6;
75     private static final int COLUMN_PROTOCOL     = 7;
76     private static final int COLUMN_ROAMING_PROTOCOL = 8;
77     private static final int COLUMN_AUTH_TYPE    = 9;
78     private static final int COLUMN_MVNO_TYPE    = 10;
79     private static final int COLUMN_MVNO_MATCH_DATA = 11;
80     private static final int COLUMN_PROXY        = 12;
81     private static final int COLUMN_PORT         = 13;
82     private static final int COLUMN_SERVER       = 14;
83     private static final int COLUMN_USER         = 15;
84     private static final int COLUMN_PASSWORD     = 16;
85 
86 
87     /**
88      * Load APN settings from system
89      *
90      * @param context
91      * @param apnName the optional APN name to match
92      */
load(Context context, String apnName, int subId)93     public static ApnSettings load(Context context, String apnName, int subId)
94             throws ApnException {
95         if (Log.isLoggable(TAG, Log.VERBOSE)) {
96             Log.v(TAG, "ApnSettings: apnName " + apnName);
97         }
98         // TODO: CURRENT semantics is currently broken in telephony. Revive this when it is fixed.
99         //String selection = Telephony.Carriers.CURRENT + " IS NOT NULL";
100         String selection = null;
101         String[] selectionArgs = null;
102         apnName = apnName != null ? apnName.trim() : null;
103         if (!TextUtils.isEmpty(apnName)) {
104             //selection += " AND " + Telephony.Carriers.APN + "=?";
105             selection = Telephony.Carriers.APN + "=?";
106             selectionArgs = new String[]{ apnName };
107         }
108         Cursor cursor = null;
109         try {
110             cursor = SqliteWrapper.query(
111                     context,
112                     context.getContentResolver(),
113                     Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "/subId/" + subId),
114                     APN_PROJECTION,
115                     selection,
116                     selectionArgs,
117                     null/*sortOrder*/);
118             if (cursor != null) {
119                 String mmscUrl = null;
120                 String proxyAddress = null;
121                 int proxyPort = -1;
122                 while (cursor.moveToNext()) {
123                     // Read values from APN settings
124                     if (isValidApnType(
125                             cursor.getString(COLUMN_TYPE), PhoneConstants.APN_TYPE_MMS)) {
126                         mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC));
127                         if (TextUtils.isEmpty(mmscUrl)) {
128                             continue;
129                         }
130                         mmscUrl = NetworkUtils.trimV4AddrZeros(mmscUrl);
131                         try {
132                             new URI(mmscUrl);
133                         } catch (URISyntaxException e) {
134                             throw new ApnException("Invalid MMSC url " + mmscUrl);
135                         }
136                         proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY));
137                         if (!TextUtils.isEmpty(proxyAddress)) {
138                             proxyAddress = NetworkUtils.trimV4AddrZeros(proxyAddress);
139                             final String portString =
140                                     trimWithNullCheck(cursor.getString(COLUMN_MMSPORT));
141                             if (portString != null) {
142                                 try {
143                                     proxyPort = Integer.parseInt(portString);
144                                 } catch (NumberFormatException e) {
145                                     Log.e(TAG, "Invalid port " + portString);
146                                     throw new ApnException("Invalid port " + portString);
147                                 }
148                             }
149                         }
150                         return new ApnSettings(
151                                 mmscUrl, proxyAddress, proxyPort, getDebugText(cursor));
152                     }
153                 }
154 
155             }
156         } finally {
157             if (cursor != null) {
158                 cursor.close();
159             }
160         }
161         throw new ApnException("Can not find valid APN");
162     }
163 
getDebugText(Cursor cursor)164     private static String getDebugText(Cursor cursor) {
165         final StringBuilder sb = new StringBuilder();
166         sb.append("APN [");
167         for (int i = 0; i < cursor.getColumnCount(); i++) {
168             final String name = cursor.getColumnName(i);
169             final String value = cursor.getString(i);
170             if (TextUtils.isEmpty(value)) {
171                 continue;
172             }
173             if (i > 0) {
174                 sb.append(' ');
175             }
176             sb.append(name).append('=').append(value);
177         }
178         sb.append("]");
179         return sb.toString();
180     }
181 
trimWithNullCheck(String value)182     private static String trimWithNullCheck(String value) {
183         return value != null ? value.trim() : null;
184     }
185 
ApnSettings(String mmscUrl, String proxyAddr, int proxyPort, String debugText)186     public ApnSettings(String mmscUrl, String proxyAddr, int proxyPort, String debugText) {
187         mServiceCenter = mmscUrl;
188         mProxyAddress = proxyAddr;
189         mProxyPort = proxyPort;
190         mDebugText = debugText;
191    }
192 
getMmscUrl()193     public String getMmscUrl() {
194         return mServiceCenter;
195     }
196 
getProxyAddress()197     public String getProxyAddress() {
198         return mProxyAddress;
199     }
200 
getProxyPort()201     public int getProxyPort() {
202         return mProxyPort;
203     }
204 
isProxySet()205     public boolean isProxySet() {
206         return !TextUtils.isEmpty(mProxyAddress);
207     }
208 
isValidApnType(String types, String requestType)209     private static boolean isValidApnType(String types, String requestType) {
210         // If APN type is unspecified, assume APN_TYPE_ALL.
211         if (TextUtils.isEmpty(types)) {
212             return true;
213         }
214         for (String type : types.split(",")) {
215             type = type.trim();
216             if (type.equals(requestType) || type.equals(PhoneConstants.APN_TYPE_ALL)) {
217                 return true;
218             }
219         }
220         return false;
221     }
222 
toString()223     public String toString() {
224         return mDebugText;
225     }
226 }
227