1 /* 2 * Copyright 2018 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.internal.telephony.dataconnection; 18 19 import android.hardware.radio.V1_0.SetupDataCallResult; 20 import android.net.LinkAddress; 21 import android.net.LinkProperties; 22 import android.net.NetworkUtils; 23 import android.os.AsyncResult; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.telephony.Rlog; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.data.DataCallResponse; 31 import android.telephony.data.DataProfile; 32 import android.telephony.data.DataService; 33 import android.telephony.data.DataServiceCallback; 34 import android.text.TextUtils; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.PhoneFactory; 39 40 import java.net.Inet4Address; 41 import java.net.InetAddress; 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.HashMap; 45 import java.util.List; 46 import java.util.Map; 47 48 /** 49 * This class represents cellular data service which handles telephony data requests and response 50 * from the cellular modem. 51 */ 52 public class CellularDataService extends DataService { 53 private static final String TAG = CellularDataService.class.getSimpleName(); 54 55 private static final boolean DBG = false; 56 57 private static final int SETUP_DATA_CALL_COMPLETE = 1; 58 private static final int DEACTIVATE_DATA_ALL_COMPLETE = 2; 59 private static final int SET_INITIAL_ATTACH_APN_COMPLETE = 3; 60 private static final int SET_DATA_PROFILE_COMPLETE = 4; 61 private static final int GET_DATA_CALL_LIST_COMPLETE = 5; 62 private static final int DATA_CALL_LIST_CHANGED = 6; 63 64 private class CellularDataServiceProvider extends DataService.DataServiceProvider { 65 66 private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>(); 67 68 private final Looper mLooper; 69 70 private final Handler mHandler; 71 72 private final Phone mPhone; 73 74 private CellularDataServiceProvider(int slotId) { 75 super(slotId); 76 77 mPhone = PhoneFactory.getPhone(getSlotId()); 78 79 HandlerThread thread = new HandlerThread(CellularDataService.class.getSimpleName()); 80 thread.start(); 81 mLooper = thread.getLooper(); 82 mHandler = new Handler(mLooper) { 83 @Override 84 public void handleMessage(Message message) { 85 DataServiceCallback callback = mCallbackMap.remove(message); 86 87 AsyncResult ar = (AsyncResult) message.obj; 88 switch (message.what) { 89 case SETUP_DATA_CALL_COMPLETE: 90 SetupDataCallResult result = (SetupDataCallResult) ar.result; 91 callback.onSetupDataCallComplete(ar.exception != null 92 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 93 : DataServiceCallback.RESULT_SUCCESS, 94 convertDataCallResult(result)); 95 break; 96 case DEACTIVATE_DATA_ALL_COMPLETE: 97 callback.onDeactivateDataCallComplete(ar.exception != null 98 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 99 : DataServiceCallback.RESULT_SUCCESS); 100 break; 101 case SET_INITIAL_ATTACH_APN_COMPLETE: 102 callback.onSetInitialAttachApnComplete(ar.exception != null 103 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 104 : DataServiceCallback.RESULT_SUCCESS); 105 break; 106 case SET_DATA_PROFILE_COMPLETE: 107 callback.onSetDataProfileComplete(ar.exception != null 108 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 109 : DataServiceCallback.RESULT_SUCCESS); 110 break; 111 case GET_DATA_CALL_LIST_COMPLETE: 112 callback.onGetDataCallListComplete( 113 ar.exception != null 114 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 115 : DataServiceCallback.RESULT_SUCCESS, 116 ar.exception != null 117 ? null 118 : getDataCallList((List<SetupDataCallResult>) ar.result) 119 ); 120 break; 121 case DATA_CALL_LIST_CHANGED: 122 notifyDataCallListChanged(getDataCallList( 123 (List<SetupDataCallResult>) ar.result)); 124 break; 125 default: 126 loge("Unexpected event: " + message.what); 127 return; 128 } 129 } 130 }; 131 132 if (DBG) log("Register for data call list changed."); 133 mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null); 134 } 135 136 private List<DataCallResponse> getDataCallList(List<SetupDataCallResult> dcList) { 137 List<DataCallResponse> dcResponseList = new ArrayList<>(); 138 for (SetupDataCallResult dcResult : dcList) { 139 dcResponseList.add(convertDataCallResult(dcResult)); 140 } 141 return dcResponseList; 142 } 143 144 @Override 145 public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming, 146 boolean allowRoaming, int reason, LinkProperties linkProperties, 147 DataServiceCallback callback) { 148 if (DBG) log("setupDataCall " + getSlotId()); 149 150 Message message = null; 151 // Only obtain the message when the caller wants a callback. If the caller doesn't care 152 // the request completed or results, then no need to pass the message down. 153 if (callback != null) { 154 message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE); 155 mCallbackMap.put(message, callback); 156 } 157 158 mPhone.mCi.setupDataCall(radioTechnology, dataProfile, isRoaming, allowRoaming, reason, 159 linkProperties, message); 160 } 161 162 @Override 163 public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) { 164 if (DBG) log("deactivateDataCall " + getSlotId()); 165 166 Message message = null; 167 // Only obtain the message when the caller wants a callback. If the caller doesn't care 168 // the request completed or results, then no need to pass the message down. 169 if (callback != null) { 170 message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE); 171 mCallbackMap.put(message, callback); 172 } 173 174 mPhone.mCi.deactivateDataCall(cid, reason, message); 175 } 176 177 @Override 178 public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, 179 DataServiceCallback callback) { 180 if (DBG) log("setInitialAttachApn " + getSlotId()); 181 182 Message message = null; 183 // Only obtain the message when the caller wants a callback. If the caller doesn't care 184 // the request completed or results, then no need to pass the message down. 185 if (callback != null) { 186 message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE); 187 mCallbackMap.put(message, callback); 188 } 189 190 mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message); 191 } 192 193 @Override 194 public void setDataProfile(List<DataProfile> dps, boolean isRoaming, 195 DataServiceCallback callback) { 196 if (DBG) log("setDataProfile " + getSlotId()); 197 198 Message message = null; 199 // Only obtain the message when the caller wants a callback. If the caller doesn't care 200 // the request completed or results, then no need to pass the message down. 201 if (callback != null) { 202 message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE); 203 mCallbackMap.put(message, callback); 204 } 205 206 mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message); 207 } 208 209 @Override 210 public void getDataCallList(DataServiceCallback callback) { 211 if (DBG) log("getDataCallList " + getSlotId()); 212 213 Message message = null; 214 // Only obtain the message when the caller wants a callback. If the caller doesn't care 215 // the request completed or results, then no need to pass the message down. 216 if (callback != null) { 217 message = Message.obtain(mHandler, GET_DATA_CALL_LIST_COMPLETE); 218 mCallbackMap.put(message, callback); 219 } 220 mPhone.mCi.getDataCallList(message); 221 } 222 } 223 224 @Override 225 public DataServiceProvider createDataServiceProvider(int slotId) { 226 log("Cellular data service created for slot " + slotId); 227 if (!SubscriptionManager.isValidSlotIndex(slotId)) { 228 loge("Tried to cellular data service with invalid slotId " + slotId); 229 return null; 230 } 231 return new CellularDataServiceProvider(slotId); 232 } 233 234 /** 235 * Convert SetupDataCallResult defined in types.hal into DataCallResponse 236 * @param dcResult setup data call result 237 * @return converted DataCallResponse object 238 */ 239 @VisibleForTesting 240 public DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) { 241 if (dcResult == null) return null; 242 243 // Process address 244 String[] addresses = null; 245 if (!TextUtils.isEmpty(dcResult.addresses)) { 246 addresses = dcResult.addresses.split("\\s+"); 247 } 248 249 List<LinkAddress> laList = new ArrayList<>(); 250 if (addresses != null) { 251 for (String address : addresses) { 252 address = address.trim(); 253 if (address.isEmpty()) continue; 254 255 try { 256 LinkAddress la; 257 // Check if the address contains prefix length. If yes, LinkAddress 258 // can parse that. 259 if (address.split("/").length == 2) { 260 la = new LinkAddress(address); 261 } else { 262 InetAddress ia = NetworkUtils.numericToInetAddress(address); 263 la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128); 264 } 265 266 laList.add(la); 267 } catch (IllegalArgumentException e) { 268 loge("Unknown address: " + address + ", exception = " + e); 269 } 270 } 271 } 272 273 // Process dns 274 String[] dnses = null; 275 if (!TextUtils.isEmpty(dcResult.dnses)) { 276 dnses = dcResult.dnses.split("\\s+"); 277 } 278 279 List<InetAddress> dnsList = new ArrayList<>(); 280 if (dnses != null) { 281 for (String dns : dnses) { 282 dns = dns.trim(); 283 InetAddress ia; 284 try { 285 ia = NetworkUtils.numericToInetAddress(dns); 286 dnsList.add(ia); 287 } catch (IllegalArgumentException e) { 288 loge("Unknown dns: " + dns + ", exception = " + e); 289 } 290 } 291 } 292 293 // Process gateway 294 String[] gateways = null; 295 if (!TextUtils.isEmpty(dcResult.gateways)) { 296 gateways = dcResult.gateways.split("\\s+"); 297 } 298 299 List<InetAddress> gatewayList = new ArrayList<>(); 300 if (gateways != null) { 301 for (String gateway : gateways) { 302 gateway = gateway.trim(); 303 InetAddress ia; 304 try { 305 ia = NetworkUtils.numericToInetAddress(gateway); 306 gatewayList.add(ia); 307 } catch (IllegalArgumentException e) { 308 loge("Unknown gateway: " + gateway + ", exception = " + e); 309 } 310 } 311 } 312 313 return new DataCallResponse(dcResult.status, 314 dcResult.suggestedRetryTime, 315 dcResult.cid, 316 dcResult.active, 317 dcResult.type, 318 dcResult.ifname, 319 laList, 320 dnsList, 321 gatewayList, 322 new ArrayList<>(Arrays.asList(dcResult.pcscf.trim().split("\\s+"))), 323 dcResult.mtu 324 ); 325 } 326 327 private void log(String s) { 328 Rlog.d(TAG, s); 329 } 330 331 private void loge(String s) { 332 Rlog.e(TAG, s); 333 } 334 } 335