The HashMap object mCallbackMap in CellularDataService is a shared resource between DataService's handler thread and CellularDataService's handler thread. The update operations (put and remove) has synchronization issue. This CL fixes the issue by letting CellularDataService shares the looper/handler thread of DataService (but keeping its own handler). And thus all the operations of mCallbackMap are handled serializally in DataService's handler thread. One alternative solution is to move all operations of the mCallbackMap into CellularDataService's handler thread to make it thread-safe. But it's difficult for close() method which need to clear mCallbackMap and shutdown the handler thread. The other alternative solution is to replace HashMap with ConcurrentHashMap for mCallbackMap. This do make mCallbackMap thread-safe but do not change the fact that it is still a shared resource between two threads. Bug: 151103522 Test: atest FrameworksTelephonyTests Change-Id: I7f5a507c2a79c7c77c5c2d7d3be147af80dc9f69
320 lines
12 KiB
Java
320 lines
12 KiB
Java
/*
|
|
* Copyright 2017 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 android.telephony;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SdkConstant;
|
|
import android.annotation.SystemApi;
|
|
import android.app.Service;
|
|
import android.content.Intent;
|
|
import android.os.Handler;
|
|
import android.os.HandlerThread;
|
|
import android.os.IBinder;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.os.RemoteException;
|
|
import android.telephony.NetworkRegistrationInfo.Domain;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.telephony.Rlog;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Base class of network service. Services that extend NetworkService must register the service in
|
|
* their AndroidManifest to be detected by the framework. They must be protected by the permission
|
|
* "android.permission.BIND_TELEPHONY_NETWORK_SERVICE". The network service definition in the
|
|
* manifest must follow the following format:
|
|
* ...
|
|
* <service android:name=".xxxNetworkService"
|
|
* android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
|
|
* <intent-filter>
|
|
* <action android:name="android.telephony.NetworkService" />
|
|
* </intent-filter>
|
|
* </service>
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public abstract class NetworkService extends Service {
|
|
|
|
private final String TAG = NetworkService.class.getSimpleName();
|
|
|
|
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
|
|
public static final String SERVICE_INTERFACE = "android.telephony.NetworkService";
|
|
|
|
private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1;
|
|
private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2;
|
|
private static final int NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS = 3;
|
|
private static final int NETWORK_SERVICE_GET_REGISTRATION_INFO = 4;
|
|
private static final int NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE = 5;
|
|
private static final int NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE = 6;
|
|
private static final int NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED = 7;
|
|
|
|
|
|
private final HandlerThread mHandlerThread;
|
|
|
|
private final NetworkServiceHandler mHandler;
|
|
|
|
private final SparseArray<NetworkServiceProvider> mServiceMap = new SparseArray<>();
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@VisibleForTesting
|
|
public final INetworkServiceWrapper mBinder = new INetworkServiceWrapper();
|
|
|
|
/**
|
|
* The abstract class of the actual network service implementation. The network service provider
|
|
* must extend this class to support network connection. Note that each instance of network
|
|
* service is associated with one physical SIM slot.
|
|
*/
|
|
public abstract class NetworkServiceProvider implements AutoCloseable {
|
|
private final int mSlotIndex;
|
|
|
|
private final List<INetworkServiceCallback>
|
|
mNetworkRegistrationInfoChangedCallbacks = new ArrayList<>();
|
|
|
|
/**
|
|
* Constructor
|
|
* @param slotIndex SIM slot id the data service provider associated with.
|
|
*/
|
|
public NetworkServiceProvider(int slotIndex) {
|
|
mSlotIndex = slotIndex;
|
|
}
|
|
|
|
/**
|
|
* @return SIM slot index the network service associated with.
|
|
*/
|
|
public final int getSlotIndex() {
|
|
return mSlotIndex;
|
|
}
|
|
|
|
/**
|
|
* Request network registration info. The result will be passed to the callback.
|
|
*
|
|
* @param domain Network domain
|
|
* @param callback The callback for reporting network registration info
|
|
*/
|
|
public void requestNetworkRegistrationInfo(@Domain int domain,
|
|
@NonNull NetworkServiceCallback callback) {
|
|
callback.onRequestNetworkRegistrationInfoComplete(
|
|
NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
|
|
}
|
|
|
|
/**
|
|
* Notify the system that network registration info is changed.
|
|
*/
|
|
public final void notifyNetworkRegistrationInfoChanged() {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED,
|
|
mSlotIndex, 0, null).sendToTarget();
|
|
}
|
|
|
|
private void registerForInfoChanged(@NonNull INetworkServiceCallback callback) {
|
|
synchronized (mNetworkRegistrationInfoChangedCallbacks) {
|
|
mNetworkRegistrationInfoChangedCallbacks.add(callback);
|
|
}
|
|
}
|
|
|
|
private void unregisterForInfoChanged(@NonNull INetworkServiceCallback callback) {
|
|
synchronized (mNetworkRegistrationInfoChangedCallbacks) {
|
|
mNetworkRegistrationInfoChangedCallbacks.remove(callback);
|
|
}
|
|
}
|
|
|
|
private void notifyInfoChangedToCallbacks() {
|
|
for (INetworkServiceCallback callback : mNetworkRegistrationInfoChangedCallbacks) {
|
|
try {
|
|
callback.onNetworkStateChanged();
|
|
} catch (RemoteException exception) {
|
|
// Doing nothing.
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when the instance of network service is destroyed (e.g. got unbind or binder died)
|
|
* or when the network service provider is removed. The extended class should implement this
|
|
* method to perform cleanup works.
|
|
*/
|
|
@Override
|
|
public abstract void close();
|
|
}
|
|
|
|
private class NetworkServiceHandler extends Handler {
|
|
|
|
NetworkServiceHandler(Looper looper) {
|
|
super(looper);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message message) {
|
|
final int slotIndex = message.arg1;
|
|
final INetworkServiceCallback callback = (INetworkServiceCallback) message.obj;
|
|
|
|
NetworkServiceProvider serviceProvider = mServiceMap.get(slotIndex);
|
|
|
|
switch (message.what) {
|
|
case NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER:
|
|
// If the service provider doesn't exist yet, we try to create it.
|
|
if (serviceProvider == null) {
|
|
mServiceMap.put(slotIndex, onCreateNetworkServiceProvider(slotIndex));
|
|
}
|
|
break;
|
|
case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
|
|
// If the service provider doesn't exist yet, we try to create it.
|
|
if (serviceProvider != null) {
|
|
serviceProvider.close();
|
|
mServiceMap.remove(slotIndex);
|
|
}
|
|
break;
|
|
case NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS:
|
|
for (int i = 0; i < mServiceMap.size(); i++) {
|
|
serviceProvider = mServiceMap.get(i);
|
|
if (serviceProvider != null) {
|
|
serviceProvider.close();
|
|
}
|
|
}
|
|
mServiceMap.clear();
|
|
break;
|
|
case NETWORK_SERVICE_GET_REGISTRATION_INFO:
|
|
if (serviceProvider == null) break;
|
|
int domainId = message.arg2;
|
|
serviceProvider.requestNetworkRegistrationInfo(domainId,
|
|
new NetworkServiceCallback(callback));
|
|
|
|
break;
|
|
case NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE:
|
|
if (serviceProvider == null) break;
|
|
serviceProvider.registerForInfoChanged(callback);
|
|
break;
|
|
case NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE:
|
|
if (serviceProvider == null) break;
|
|
serviceProvider.unregisterForInfoChanged(callback);
|
|
break;
|
|
case NETWORK_SERVICE_INDICATION_NETWORK_INFO_CHANGED:
|
|
if (serviceProvider == null) break;
|
|
serviceProvider.notifyInfoChangedToCallbacks();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Default constructor.
|
|
*/
|
|
public NetworkService() {
|
|
mHandlerThread = new HandlerThread(TAG);
|
|
mHandlerThread.start();
|
|
|
|
mHandler = new NetworkServiceHandler(mHandlerThread.getLooper());
|
|
log("network service created");
|
|
}
|
|
|
|
/**
|
|
* Create the instance of {@link NetworkServiceProvider}. Network service provider must override
|
|
* this method to facilitate the creation of {@link NetworkServiceProvider} instances. The system
|
|
* will call this method after binding the network service for each active SIM slot id.
|
|
*
|
|
* This methead is guaranteed to be invoked in {@link NetworkService}'s internal handler thread
|
|
* whose looper can be retrieved with {@link Looper.myLooper()} when override this method.
|
|
*
|
|
* @param slotIndex SIM slot id the network service associated with.
|
|
* @return Network service object. Null if failed to create the provider (e.g. invalid slot
|
|
* index)
|
|
*/
|
|
@Nullable
|
|
public abstract NetworkServiceProvider onCreateNetworkServiceProvider(int slotIndex);
|
|
|
|
@Override
|
|
public IBinder onBind(Intent intent) {
|
|
if (intent == null || !SERVICE_INTERFACE.equals(intent.getAction())) {
|
|
loge("Unexpected intent " + intent);
|
|
return null;
|
|
}
|
|
|
|
return mBinder;
|
|
}
|
|
|
|
@Override
|
|
public boolean onUnbind(Intent intent) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_ALL_NETWORK_SERVICE_PROVIDERS, 0,
|
|
0, null).sendToTarget();
|
|
|
|
return false;
|
|
}
|
|
|
|
/** @hide */
|
|
@Override
|
|
public void onDestroy() {
|
|
mHandlerThread.quit();
|
|
super.onDestroy();
|
|
}
|
|
|
|
/**
|
|
* A wrapper around INetworkService that forwards calls to implementations of
|
|
* {@link NetworkService}.
|
|
*/
|
|
private class INetworkServiceWrapper extends INetworkService.Stub {
|
|
|
|
@Override
|
|
public void createNetworkServiceProvider(int slotIndex) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER, slotIndex,
|
|
0, null).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public void removeNetworkServiceProvider(int slotIndex) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER, slotIndex,
|
|
0, null).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public void requestNetworkRegistrationInfo(int slotIndex, int domain,
|
|
INetworkServiceCallback callback) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_GET_REGISTRATION_INFO, slotIndex,
|
|
domain, callback).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public void registerForNetworkRegistrationInfoChanged(
|
|
int slotIndex, INetworkServiceCallback callback) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_REGISTER_FOR_INFO_CHANGE, slotIndex,
|
|
0, callback).sendToTarget();
|
|
}
|
|
|
|
@Override
|
|
public void unregisterForNetworkRegistrationInfoChanged(
|
|
int slotIndex, INetworkServiceCallback callback) {
|
|
mHandler.obtainMessage(NETWORK_SERVICE_UNREGISTER_FOR_INFO_CHANGE, slotIndex,
|
|
0, callback).sendToTarget();
|
|
}
|
|
}
|
|
|
|
private final void log(String s) {
|
|
Rlog.d(TAG, s);
|
|
}
|
|
|
|
private final void loge(String s) {
|
|
Rlog.e(TAG, s);
|
|
}
|
|
}
|