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