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