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