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