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.net.LinkProperties;
20 import android.os.AsyncResult;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.telephony.Rlog;
26 import android.telephony.SubscriptionManager;
27 import android.telephony.data.DataCallResponse;
28 import android.telephony.data.DataProfile;
29 import android.telephony.data.DataService;
30 import android.telephony.data.DataServiceCallback;
31 
32 import com.android.internal.telephony.Phone;
33 import com.android.internal.telephony.PhoneFactory;
34 
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 
39 /**
40  * This class represents cellular data service which handles telephony data requests and response
41  * from the cellular modem.
42  */
43 public class CellularDataService extends DataService {
44     private static final String TAG = CellularDataService.class.getSimpleName();
45 
46     private static final boolean DBG = false;
47 
48     private static final int SETUP_DATA_CALL_COMPLETE               = 1;
49     private static final int DEACTIVATE_DATA_ALL_COMPLETE           = 2;
50     private static final int SET_INITIAL_ATTACH_APN_COMPLETE        = 3;
51     private static final int SET_DATA_PROFILE_COMPLETE              = 4;
52     private static final int REQUEST_DATA_CALL_LIST_COMPLETE        = 5;
53     private static final int DATA_CALL_LIST_CHANGED                 = 6;
54 
55     private class CellularDataServiceProvider extends DataService.DataServiceProvider {
56 
57         private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>();
58 
59         private final Looper mLooper;
60 
61         private final Handler mHandler;
62 
63         private final HandlerThread mHandlerThread;
64 
65         private final Phone mPhone;
66 
CellularDataServiceProvider(int slotId)67         private CellularDataServiceProvider(int slotId) {
68             super(slotId);
69 
70             mPhone = PhoneFactory.getPhone(getSlotIndex());
71 
72             mHandlerThread = new HandlerThread(CellularDataService.class.getSimpleName());
73             mHandlerThread.start();
74             mLooper = mHandlerThread.getLooper();
75             mHandler = new Handler(mLooper) {
76                 @Override
77                 public void handleMessage(Message message) {
78                     DataServiceCallback callback = mCallbackMap.remove(message);
79 
80                     AsyncResult ar = (AsyncResult) message.obj;
81                     switch (message.what) {
82                         case SETUP_DATA_CALL_COMPLETE:
83                             DataCallResponse response = (DataCallResponse) ar.result;
84                             callback.onSetupDataCallComplete(ar.exception != null
85                                     ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
86                                     : DataServiceCallback.RESULT_SUCCESS,
87                                     response);
88                             break;
89                         case DEACTIVATE_DATA_ALL_COMPLETE:
90                             callback.onDeactivateDataCallComplete(ar.exception != null
91                                     ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
92                                     : DataServiceCallback.RESULT_SUCCESS);
93                             break;
94                         case SET_INITIAL_ATTACH_APN_COMPLETE:
95                             callback.onSetInitialAttachApnComplete(ar.exception != null
96                                     ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
97                                     : DataServiceCallback.RESULT_SUCCESS);
98                             break;
99                         case SET_DATA_PROFILE_COMPLETE:
100                             callback.onSetDataProfileComplete(ar.exception != null
101                                     ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
102                                     : DataServiceCallback.RESULT_SUCCESS);
103                             break;
104                         case REQUEST_DATA_CALL_LIST_COMPLETE:
105                             callback.onRequestDataCallListComplete(
106                                     ar.exception != null
107                                             ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
108                                             : DataServiceCallback.RESULT_SUCCESS,
109                                     ar.exception != null
110                                             ? null : (List<DataCallResponse>) ar.result
111                                     );
112                             break;
113                         case DATA_CALL_LIST_CHANGED:
114                             notifyDataCallListChanged((List<DataCallResponse>) ar.result);
115                             break;
116                         default:
117                             loge("Unexpected event: " + message.what);
118                             return;
119                     }
120                 }
121             };
122 
123             if (DBG) log("Register for data call list changed.");
124             mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null);
125         }
126 
127         @Override
setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, DataServiceCallback callback)128         public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
129                                   boolean allowRoaming, int reason, LinkProperties linkProperties,
130                                   DataServiceCallback callback) {
131             if (DBG) log("setupDataCall " + getSlotIndex());
132 
133             Message message = null;
134             // Only obtain the message when the caller wants a callback. If the caller doesn't care
135             // the request completed or results, then no need to pass the message down.
136             if (callback != null) {
137                 message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE);
138                 mCallbackMap.put(message, callback);
139             }
140 
141             mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming,
142                     reason, linkProperties, message);
143         }
144 
145         @Override
deactivateDataCall(int cid, int reason, DataServiceCallback callback)146         public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) {
147             if (DBG) log("deactivateDataCall " + getSlotIndex());
148 
149             Message message = null;
150             // Only obtain the message when the caller wants a callback. If the caller doesn't care
151             // the request completed or results, then no need to pass the message down.
152             if (callback != null) {
153                 message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
154                 mCallbackMap.put(message, callback);
155             }
156 
157             mPhone.mCi.deactivateDataCall(cid, reason, message);
158         }
159 
160         @Override
setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, DataServiceCallback callback)161         public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
162                                         DataServiceCallback callback) {
163             if (DBG) log("setInitialAttachApn " + getSlotIndex());
164 
165             Message message = null;
166             // Only obtain the message when the caller wants a callback. If the caller doesn't care
167             // the request completed or results, then no need to pass the message down.
168             if (callback != null) {
169                 message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE);
170                 mCallbackMap.put(message, callback);
171             }
172 
173             mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message);
174         }
175 
176         @Override
setDataProfile(List<DataProfile> dps, boolean isRoaming, DataServiceCallback callback)177         public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
178                                    DataServiceCallback callback) {
179             if (DBG) log("setDataProfile " + getSlotIndex());
180 
181             Message message = null;
182             // Only obtain the message when the caller wants a callback. If the caller doesn't care
183             // the request completed or results, then no need to pass the message down.
184             if (callback != null) {
185                 message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE);
186                 mCallbackMap.put(message, callback);
187             }
188 
189             mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message);
190         }
191 
192         @Override
requestDataCallList(DataServiceCallback callback)193         public void requestDataCallList(DataServiceCallback callback) {
194             if (DBG) log("requestDataCallList " + getSlotIndex());
195 
196             Message message = null;
197             // Only obtain the message when the caller wants a callback. If the caller doesn't care
198             // the request completed or results, then no need to pass the message down.
199             if (callback != null) {
200                 message = Message.obtain(mHandler, REQUEST_DATA_CALL_LIST_COMPLETE);
201                 mCallbackMap.put(message, callback);
202             }
203             mPhone.mCi.getDataCallList(message);
204         }
205 
206         @Override
close()207         public void close() {
208             mPhone.mCi.unregisterForDataCallListChanged(mHandler);
209             mHandlerThread.quit();
210         }
211     }
212 
213     @Override
onCreateDataServiceProvider(int slotIndex)214     public DataServiceProvider onCreateDataServiceProvider(int slotIndex) {
215         log("Cellular data service created for slot " + slotIndex);
216         if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
217             loge("Tried to cellular data service with invalid slotId " + slotIndex);
218             return null;
219         }
220         return new CellularDataServiceProvider(slotIndex);
221     }
222 
log(String s)223     private void log(String s) {
224         Rlog.d(TAG, s);
225     }
226 
loge(String s)227     private void loge(String s) {
228         Rlog.e(TAG, s);
229     }
230 }
231