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.Uri; 22 import android.provider.Telephony; 23 import android.telephony.data.ApnSetting; 24 import android.text.TextUtils; 25 26 import com.android.mms.service.exception.ApnException; 27 import com.android.net.module.util.Inet4AddressUtils; 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 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 * 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 104 try (Cursor cursor = context.getContentResolver().query( 105 Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, String.valueOf(subId)), 106 APN_PROJECTION, 107 selection, 108 selectionArgs, 109 null/*sortOrder*/)) { 110 111 ApnSettings settings = getApnSettingsFromCursor(cursor, requestId); 112 if (settings != null) { 113 return settings; 114 } 115 } 116 throw new ApnException("Can not find valid APN"); 117 } 118 getApnSettingsFromCursor(Cursor cursor, String requestId)119 private static ApnSettings getApnSettingsFromCursor(Cursor cursor, String requestId) 120 throws ApnException { 121 if (cursor == null) { 122 return null; 123 } 124 125 // Default proxy port to 80 126 int proxyPort = 80; 127 while (cursor.moveToNext()) { 128 // Read values from APN settings 129 if (isValidApnType( 130 cursor.getString(COLUMN_TYPE), ApnSetting.TYPE_MMS_STRING)) { 131 String mmscUrl = trimWithNullCheck(cursor.getString(COLUMN_MMSC)); 132 if (TextUtils.isEmpty(mmscUrl)) { 133 continue; 134 } 135 mmscUrl = Inet4AddressUtils.trimAddressZeros(mmscUrl); 136 try { 137 new URI(mmscUrl); 138 } catch (URISyntaxException e) { 139 throw new ApnException("Invalid MMSC url " + mmscUrl); 140 } 141 String proxyAddress = trimWithNullCheck(cursor.getString(COLUMN_MMSPROXY)); 142 if (!TextUtils.isEmpty(proxyAddress)) { 143 proxyAddress = Inet4AddressUtils.trimAddressZeros(proxyAddress); 144 final String portString = 145 trimWithNullCheck(cursor.getString(COLUMN_MMSPORT)); 146 if (!TextUtils.isEmpty(portString)) { 147 try { 148 proxyPort = Integer.parseInt(portString); 149 } catch (NumberFormatException e) { 150 LogUtil.e(requestId, "Invalid port " + portString + ", use 80"); 151 } 152 } 153 } 154 return new ApnSettings( 155 mmscUrl, proxyAddress, proxyPort, getDebugText(cursor)); 156 } 157 } 158 return null; 159 } 160 getDebugText(Cursor cursor)161 private static String getDebugText(Cursor cursor) { 162 final StringBuilder sb = new StringBuilder(); 163 sb.append("APN ["); 164 for (int i = 0; i < cursor.getColumnCount(); i++) { 165 final String name = cursor.getColumnName(i); 166 final String value = cursor.getString(i); 167 if (TextUtils.isEmpty(value)) { 168 continue; 169 } 170 if (i > 0) { 171 sb.append(' '); 172 } 173 sb.append(name).append('=').append(value); 174 } 175 sb.append("]"); 176 return sb.toString(); 177 } 178 trimWithNullCheck(String value)179 private static String trimWithNullCheck(String value) { 180 return value != null ? value.trim() : null; 181 } 182 ApnSettings(String mmscUrl, String proxyAddr, int proxyPort, String debugText)183 public ApnSettings(String mmscUrl, String proxyAddr, int proxyPort, String debugText) { 184 mServiceCenter = mmscUrl; 185 mProxyAddress = proxyAddr; 186 mProxyPort = proxyPort; 187 mDebugText = debugText; 188 } 189 getMmscUrl()190 public String getMmscUrl() { 191 return mServiceCenter; 192 } 193 getProxyAddress()194 public String getProxyAddress() { 195 return mProxyAddress; 196 } 197 getProxyPort()198 public int getProxyPort() { 199 return mProxyPort; 200 } 201 isProxySet()202 public boolean isProxySet() { 203 return !TextUtils.isEmpty(mProxyAddress); 204 } 205 isValidApnType(String types, String requestType)206 private static boolean isValidApnType(String types, String requestType) { 207 // If APN type is unspecified, assume TYPE_ALL_STRING. 208 if (TextUtils.isEmpty(types)) { 209 return true; 210 } 211 for (String type : types.split(",")) { 212 type = type.trim(); 213 if (type.equals(requestType) || type.equals(ApnSetting.TYPE_ALL_STRING)) { 214 return true; 215 } 216 } 217 return false; 218 } 219 toString()220 public String toString() { 221 return mDebugText; 222 } 223 } 224