| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony.dataconnection; |
| |
| import android.net.LinkProperties; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.data.DataCallResponse; |
| import android.telephony.data.DataProfile; |
| import android.telephony.data.DataService; |
| import android.telephony.data.DataServiceCallback; |
| |
| import com.android.internal.telephony.Phone; |
| import com.android.internal.telephony.PhoneFactory; |
| import com.android.telephony.Rlog; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * This class represents cellular data service which handles telephony data requests and response |
| * from the cellular modem. |
| */ |
| public class CellularDataService extends DataService { |
| private static final String TAG = CellularDataService.class.getSimpleName(); |
| |
| private static final boolean DBG = false; |
| |
| private static final int SETUP_DATA_CALL_COMPLETE = 1; |
| private static final int DEACTIVATE_DATA_ALL_COMPLETE = 2; |
| private static final int SET_INITIAL_ATTACH_APN_COMPLETE = 3; |
| private static final int SET_DATA_PROFILE_COMPLETE = 4; |
| private static final int REQUEST_DATA_CALL_LIST_COMPLETE = 5; |
| private static final int DATA_CALL_LIST_CHANGED = 6; |
| |
| private class CellularDataServiceProvider extends DataService.DataServiceProvider { |
| |
| private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>(); |
| |
| private final Handler mHandler; |
| |
| private final Phone mPhone; |
| |
| private CellularDataServiceProvider(int slotId) { |
| super(slotId); |
| |
| mPhone = PhoneFactory.getPhone(getSlotIndex()); |
| |
| mHandler = new Handler(Looper.myLooper()) { |
| @Override |
| public void handleMessage(Message message) { |
| DataServiceCallback callback = mCallbackMap.remove(message); |
| |
| AsyncResult ar = (AsyncResult) message.obj; |
| switch (message.what) { |
| case SETUP_DATA_CALL_COMPLETE: |
| DataCallResponse response = (DataCallResponse) ar.result; |
| callback.onSetupDataCallComplete(ar.exception != null |
| ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE |
| : DataServiceCallback.RESULT_SUCCESS, |
| response); |
| break; |
| case DEACTIVATE_DATA_ALL_COMPLETE: |
| callback.onDeactivateDataCallComplete(ar.exception != null |
| ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE |
| : DataServiceCallback.RESULT_SUCCESS); |
| break; |
| case SET_INITIAL_ATTACH_APN_COMPLETE: |
| callback.onSetInitialAttachApnComplete(ar.exception != null |
| ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE |
| : DataServiceCallback.RESULT_SUCCESS); |
| break; |
| case SET_DATA_PROFILE_COMPLETE: |
| callback.onSetDataProfileComplete(ar.exception != null |
| ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE |
| : DataServiceCallback.RESULT_SUCCESS); |
| break; |
| case REQUEST_DATA_CALL_LIST_COMPLETE: |
| callback.onRequestDataCallListComplete( |
| ar.exception != null |
| ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE |
| : DataServiceCallback.RESULT_SUCCESS, |
| ar.exception != null |
| ? null : (List<DataCallResponse>) ar.result |
| ); |
| break; |
| case DATA_CALL_LIST_CHANGED: |
| notifyDataCallListChanged((List<DataCallResponse>) ar.result); |
| break; |
| default: |
| loge("Unexpected event: " + message.what); |
| return; |
| } |
| } |
| }; |
| |
| if (DBG) log("Register for data call list changed."); |
| mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null); |
| } |
| |
| @Override |
| public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, |
| boolean allowRoaming, int reason, LinkProperties linkProperties, |
| DataServiceCallback callback) { |
| if (DBG) log("setupDataCall " + getSlotIndex()); |
| |
| Message message = null; |
| // Only obtain the message when the caller wants a callback. If the caller doesn't care |
| // the request completed or results, then no need to pass the message down. |
| if (callback != null) { |
| message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE); |
| mCallbackMap.put(message, callback); |
| } |
| |
| mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, |
| reason, linkProperties, message); |
| } |
| |
| @Override |
| public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) { |
| if (DBG) log("deactivateDataCall " + getSlotIndex()); |
| |
| Message message = null; |
| // Only obtain the message when the caller wants a callback. If the caller doesn't care |
| // the request completed or results, then no need to pass the message down. |
| if (callback != null) { |
| message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE); |
| mCallbackMap.put(message, callback); |
| } |
| |
| mPhone.mCi.deactivateDataCall(cid, reason, message); |
| } |
| |
| @Override |
| public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, |
| DataServiceCallback callback) { |
| if (DBG) log("setInitialAttachApn " + getSlotIndex()); |
| |
| Message message = null; |
| // Only obtain the message when the caller wants a callback. If the caller doesn't care |
| // the request completed or results, then no need to pass the message down. |
| if (callback != null) { |
| message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE); |
| mCallbackMap.put(message, callback); |
| } |
| |
| mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message); |
| } |
| |
| @Override |
| public void setDataProfile(List<DataProfile> dps, boolean isRoaming, |
| DataServiceCallback callback) { |
| if (DBG) log("setDataProfile " + getSlotIndex()); |
| |
| Message message = null; |
| // Only obtain the message when the caller wants a callback. If the caller doesn't care |
| // the request completed or results, then no need to pass the message down. |
| if (callback != null) { |
| message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE); |
| mCallbackMap.put(message, callback); |
| } |
| |
| mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message); |
| } |
| |
| @Override |
| public void requestDataCallList(DataServiceCallback callback) { |
| if (DBG) log("requestDataCallList " + getSlotIndex()); |
| |
| Message message = null; |
| // Only obtain the message when the caller wants a callback. If the caller doesn't care |
| // the request completed or results, then no need to pass the message down. |
| if (callback != null) { |
| message = Message.obtain(mHandler, REQUEST_DATA_CALL_LIST_COMPLETE); |
| mCallbackMap.put(message, callback); |
| } |
| mPhone.mCi.getDataCallList(message); |
| } |
| |
| @Override |
| public void close() { |
| mPhone.mCi.unregisterForDataCallListChanged(mHandler); |
| } |
| } |
| |
| @Override |
| public DataServiceProvider onCreateDataServiceProvider(int slotIndex) { |
| log("Cellular data service created for slot " + slotIndex); |
| if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { |
| loge("Tried to cellular data service with invalid slotId " + slotIndex); |
| return null; |
| } |
| return new CellularDataServiceProvider(slotIndex); |
| } |
| |
| private void log(String s) { |
| Rlog.d(TAG, s); |
| } |
| |
| private void loge(String s) { |
| Rlog.e(TAG, s); |
| } |
| } |