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 static android.telephony.data.DataServiceCallback.RESULT_SUCCESS; 20 21 import android.annotation.Nullable; 22 import android.net.LinkProperties; 23 import android.os.AsyncResult; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.data.DataCallResponse; 29 import android.telephony.data.DataProfile; 30 import android.telephony.data.DataService; 31 import android.telephony.data.DataServiceCallback; 32 import android.telephony.data.NetworkSliceInfo; 33 import android.telephony.data.TrafficDescriptor; 34 35 import com.android.internal.telephony.CommandException; 36 import com.android.internal.telephony.Phone; 37 import com.android.internal.telephony.PhoneFactory; 38 import com.android.telephony.Rlog; 39 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 44 /** 45 * This class represents cellular data service which handles telephony data requests and response 46 * from the cellular modem. 47 */ 48 public class CellularDataService extends DataService { 49 private static final String TAG = CellularDataService.class.getSimpleName(); 50 51 private static final boolean DBG = false; 52 53 private static final int SETUP_DATA_CALL_COMPLETE = 1; 54 private static final int DEACTIVATE_DATA_ALL_COMPLETE = 2; 55 private static final int SET_INITIAL_ATTACH_APN_COMPLETE = 3; 56 private static final int SET_DATA_PROFILE_COMPLETE = 4; 57 private static final int REQUEST_DATA_CALL_LIST_COMPLETE = 5; 58 private static final int DATA_CALL_LIST_CHANGED = 6; 59 private static final int START_HANDOVER = 7; 60 private static final int CANCEL_HANDOVER = 8; 61 private static final int APN_UNTHROTTLED = 9; 62 63 private class CellularDataServiceProvider extends DataService.DataServiceProvider { 64 65 private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>(); 66 67 private final Handler mHandler; 68 69 private final Phone mPhone; 70 CellularDataServiceProvider(int slotId)71 private CellularDataServiceProvider(int slotId) { 72 super(slotId); 73 74 mPhone = PhoneFactory.getPhone(getSlotIndex()); 75 76 mHandler = new Handler(Looper.myLooper()) { 77 @Override 78 public void handleMessage(Message message) { 79 DataServiceCallback callback = mCallbackMap.remove(message); 80 81 AsyncResult ar = (AsyncResult) message.obj; 82 switch (message.what) { 83 case SETUP_DATA_CALL_COMPLETE: 84 DataCallResponse response = (DataCallResponse) ar.result; 85 callback.onSetupDataCallComplete(ar.exception != null 86 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 87 : RESULT_SUCCESS, 88 response); 89 break; 90 case DEACTIVATE_DATA_ALL_COMPLETE: 91 callback.onDeactivateDataCallComplete(ar.exception != null 92 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 93 : RESULT_SUCCESS); 94 break; 95 case SET_INITIAL_ATTACH_APN_COMPLETE: 96 callback.onSetInitialAttachApnComplete(ar.exception != null 97 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 98 : RESULT_SUCCESS); 99 break; 100 case SET_DATA_PROFILE_COMPLETE: 101 callback.onSetDataProfileComplete(ar.exception != null 102 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 103 : RESULT_SUCCESS); 104 break; 105 case REQUEST_DATA_CALL_LIST_COMPLETE: 106 callback.onRequestDataCallListComplete( 107 ar.exception != null 108 ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE 109 : RESULT_SUCCESS, 110 ar.exception != null 111 ? null : (List<DataCallResponse>) ar.result 112 ); 113 break; 114 case DATA_CALL_LIST_CHANGED: 115 notifyDataCallListChanged((List<DataCallResponse>) ar.result); 116 break; 117 case START_HANDOVER: 118 callback.onHandoverStarted(toResultCode(ar.exception)); 119 break; 120 case CANCEL_HANDOVER: 121 callback.onHandoverCancelled(toResultCode(ar.exception)); 122 break; 123 case APN_UNTHROTTLED: 124 notifyApnUnthrottled((String) ar.result); 125 break; 126 default: 127 loge("Unexpected event: " + message.what); 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 if (DBG) log("Register for apn unthrottled."); 136 mPhone.mCi.registerForApnUnthrottled(mHandler, APN_UNTHROTTLED, null); 137 } 138 139 140 /* Converts the result code for start handover and cancel handover */ toResultCode(@ullable Throwable t)141 @DataServiceCallback.ResultCode private int toResultCode(@Nullable Throwable t) { 142 if (t == null) { 143 return RESULT_SUCCESS; 144 } else { 145 if (t instanceof CommandException) { 146 CommandException ce = (CommandException) t; 147 if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) { 148 return DataServiceCallback.RESULT_ERROR_UNSUPPORTED; 149 } else { 150 return DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE; 151 } 152 } else { 153 loge("Throwable is of type " + t.getClass().getSimpleName() 154 + " but should be CommandException"); 155 return DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE; 156 } 157 } 158 } 159 160 @Override setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, DataServiceCallback callback)161 public void setupDataCall(int accessNetworkType, DataProfile dataProfile, 162 boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, 163 int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, 164 boolean matchAllRuleAllowed, DataServiceCallback callback) { 165 if (DBG) log("setupDataCall " + getSlotIndex()); 166 167 Message message = null; 168 // Only obtain the message when the caller wants a callback. If the caller doesn't care 169 // the request completed or results, then no need to pass the message down. 170 if (callback != null) { 171 message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE); 172 mCallbackMap.put(message, callback); 173 } 174 175 mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, 176 reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor, 177 matchAllRuleAllowed, message); 178 } 179 180 @Override deactivateDataCall(int cid, int reason, DataServiceCallback callback)181 public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) { 182 if (DBG) log("deactivateDataCall " + getSlotIndex()); 183 184 Message message = null; 185 // Only obtain the message when the caller wants a callback. If the caller doesn't care 186 // the request completed or results, then no need to pass the message down. 187 if (callback != null) { 188 message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE); 189 mCallbackMap.put(message, callback); 190 } 191 192 mPhone.mCi.deactivateDataCall(cid, reason, message); 193 } 194 195 @Override setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, DataServiceCallback callback)196 public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, 197 DataServiceCallback callback) { 198 if (DBG) log("setInitialAttachApn " + getSlotIndex()); 199 200 Message message = null; 201 // Only obtain the message when the caller wants a callback. If the caller doesn't care 202 // the request completed or results, then no need to pass the message down. 203 if (callback != null) { 204 message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE); 205 mCallbackMap.put(message, callback); 206 } 207 208 mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message); 209 } 210 211 @Override setDataProfile(List<DataProfile> dps, boolean isRoaming, DataServiceCallback callback)212 public void setDataProfile(List<DataProfile> dps, boolean isRoaming, 213 DataServiceCallback callback) { 214 if (DBG) log("setDataProfile " + getSlotIndex()); 215 216 Message message = null; 217 // Only obtain the message when the caller wants a callback. If the caller doesn't care 218 // the request completed or results, then no need to pass the message down. 219 if (callback != null) { 220 message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE); 221 mCallbackMap.put(message, callback); 222 } 223 224 mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message); 225 } 226 227 @Override requestDataCallList(DataServiceCallback callback)228 public void requestDataCallList(DataServiceCallback callback) { 229 if (DBG) log("requestDataCallList " + getSlotIndex()); 230 231 Message message = null; 232 // Only obtain the message when the caller wants a callback. If the caller doesn't care 233 // the request completed or results, then no need to pass the message down. 234 if (callback != null) { 235 message = Message.obtain(mHandler, REQUEST_DATA_CALL_LIST_COMPLETE); 236 mCallbackMap.put(message, callback); 237 } 238 mPhone.mCi.getDataCallList(message); 239 } 240 241 @Override startHandover(int cid, DataServiceCallback callback)242 public void startHandover(int cid, DataServiceCallback callback) { 243 if (DBG) log("startHandover " + getSlotIndex()); 244 Message message = null; 245 // Only obtain the message when the caller wants a callback. If the caller doesn't care 246 // the request completed or results, then no need to pass the message down. 247 if (callback != null) { 248 message = Message.obtain(mHandler, START_HANDOVER); 249 mCallbackMap.put(message, callback); 250 } 251 mPhone.mCi.startHandover(message, cid); 252 } 253 254 @Override cancelHandover(int cid, DataServiceCallback callback)255 public void cancelHandover(int cid, DataServiceCallback callback) { 256 Message message = null; 257 // Only obtain the message when the caller wants a callback. If the caller doesn't care 258 // the request completed or results, then no need to pass the message down. 259 if (callback != null) { 260 message = Message.obtain(mHandler, CANCEL_HANDOVER); 261 mCallbackMap.put(message, callback); 262 } 263 mPhone.mCi.cancelHandover(message, cid); 264 } 265 266 @Override close()267 public void close() { 268 mPhone.mCi.unregisterForDataCallListChanged(mHandler); 269 } 270 } 271 272 @Override onCreateDataServiceProvider(int slotIndex)273 public DataServiceProvider onCreateDataServiceProvider(int slotIndex) { 274 log("Cellular data service created for slot " + slotIndex); 275 if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { 276 loge("Tried to cellular data service with invalid slotId " + slotIndex); 277 return null; 278 } 279 return new CellularDataServiceProvider(slotIndex); 280 } 281 log(String s)282 private void log(String s) { 283 Rlog.d(TAG, s); 284 } 285 loge(String s)286 private void loge(String s) { 287 Rlog.e(TAG, s); 288 } 289 } 290