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