1 /* 2 * Copyright 2020 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.google.android.iwlan; 18 19 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 22 import android.content.Context; 23 import android.content.Intent; 24 import android.net.ConnectivityManager; 25 import android.net.LinkProperties; 26 import android.net.Network; 27 import android.net.NetworkCapabilities; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.telephony.AccessNetworkConstants; 34 import android.telephony.NetworkRegistrationInfo; 35 import android.telephony.NetworkService; 36 import android.telephony.NetworkServiceCallback; 37 import android.telephony.SubscriptionManager; 38 import android.telephony.TelephonyManager; 39 import android.util.Log; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.List; 46 47 public class IwlanNetworkService extends NetworkService { 48 private static final String TAG = IwlanNetworkService.class.getSimpleName(); 49 private Context mContext; 50 private IwlanNetworkMonitorCallback mNetworkMonitorCallback; 51 private IwlanOnSubscriptionsChangedListener mSubsChangeListener; 52 private HandlerThread mNetworkCallbackHandlerThread; 53 private static boolean sNetworkConnected; 54 private static List<IwlanNetworkServiceProvider> sIwlanNetworkServiceProviderList = 55 new ArrayList<IwlanNetworkServiceProvider>(); 56 57 @VisibleForTesting 58 enum Transport { 59 UNSPECIFIED_NETWORK, 60 MOBILE, 61 WIFI; 62 } 63 64 private static Transport sDefaultDataTransport = Transport.UNSPECIFIED_NETWORK; 65 66 final class IwlanNetworkMonitorCallback extends ConnectivityManager.NetworkCallback { 67 /** Called when the framework connects and has declared a new network ready for use. */ 68 @Override onAvailable(Network network)69 public void onAvailable(Network network) { 70 Log.d(TAG, "onAvailable: " + network); 71 } 72 73 /** 74 * Called when the network is about to be lost, typically because there are no outstanding 75 * requests left for it. This may be paired with a {@link NetworkCallback#onAvailable} call 76 * with the new replacement network for graceful handover. This method is not guaranteed to 77 * be called before {@link NetworkCallback#onLost} is called, for example in case a network 78 * is suddenly disconnected. 79 */ 80 @Override onLosing(Network network, int maxMsToLive)81 public void onLosing(Network network, int maxMsToLive) { 82 Log.d(TAG, "onLosing: maxMsToLive: " + maxMsToLive + " network: " + network); 83 } 84 85 /** 86 * Called when a network disconnects or otherwise no longer satisfies this request or 87 * callback. 88 */ 89 @Override onLost(Network network)90 public void onLost(Network network) { 91 Log.d(TAG, "onLost: " + network); 92 IwlanNetworkService.setNetworkConnected(false, Transport.UNSPECIFIED_NETWORK); 93 } 94 95 /** Called when the network corresponding to this request changes {@link LinkProperties}. */ 96 @Override onLinkPropertiesChanged(Network network, LinkProperties linkProperties)97 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 98 Log.d(TAG, "onLinkPropertiesChanged: " + linkProperties); 99 } 100 101 /** Called when access to the specified network is blocked or unblocked. */ 102 @Override onBlockedStatusChanged(Network network, boolean blocked)103 public void onBlockedStatusChanged(Network network, boolean blocked) { 104 // TODO: check if we need to handle this 105 Log.d(TAG, "onBlockedStatusChanged: " + " BLOCKED:" + blocked); 106 } 107 108 @Override onCapabilitiesChanged( Network network, NetworkCapabilities networkCapabilities)109 public void onCapabilitiesChanged( 110 Network network, NetworkCapabilities networkCapabilities) { 111 // onCapabilitiesChanged is guaranteed to be called immediately after onAvailable per 112 // API 113 Log.d(TAG, "onCapabilitiesChanged: " + network); 114 if (networkCapabilities != null) { 115 if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { 116 IwlanNetworkService.setNetworkConnected( 117 true, IwlanNetworkService.Transport.MOBILE); 118 } else if (networkCapabilities.hasTransport(TRANSPORT_WIFI)) { 119 IwlanNetworkService.setNetworkConnected( 120 true, IwlanNetworkService.Transport.WIFI); 121 } else { 122 Log.w(TAG, "Network does not have cellular or wifi capability"); 123 } 124 } 125 } 126 } 127 128 final class IwlanOnSubscriptionsChangedListener 129 extends SubscriptionManager.OnSubscriptionsChangedListener { 130 /** 131 * Callback invoked when there is any change to any SubscriptionInfo. Typically this method 132 * invokes {@link SubscriptionManager#getActiveSubscriptionInfoList} 133 */ 134 @Override onSubscriptionsChanged()135 public void onSubscriptionsChanged() { 136 for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) { 137 np.subscriptionChanged(); 138 } 139 } 140 } 141 142 @VisibleForTesting 143 class IwlanNetworkServiceProvider extends NetworkServiceProvider { 144 private final IwlanNetworkService mIwlanNetworkService; 145 private final String SUB_TAG; 146 private boolean mIsSubActive = false; 147 private HandlerThread mHandlerThread; 148 private Handler mHandler; 149 150 private final class NSPHandler extends Handler { 151 private final String TAG = 152 IwlanNetworkService.class.getSimpleName() 153 + NSPHandler.class.getSimpleName() 154 + "[" 155 + getSlotIndex() 156 + "]"; 157 158 @Override handleMessage(Message msg)159 public void handleMessage(Message msg) { 160 Log.d(TAG, "msg.what = " + msg.what); 161 switch (msg.what) { 162 case IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT: 163 Log.d(TAG, "CROSS_SIM_CALLING_ENABLE_EVENT"); 164 notifyNetworkRegistrationInfoChanged(); 165 break; 166 case IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT: 167 Log.d(TAG, "CROSS_SIM_CALLING_DISABLE_EVENT"); 168 notifyNetworkRegistrationInfoChanged(); 169 break; 170 default: 171 Log.d(TAG, "Unknown message received!"); 172 break; 173 } 174 } 175 NSPHandler(Looper looper)176 NSPHandler(Looper looper) { 177 super(looper); 178 } 179 } 180 getLooper()181 Looper getLooper() { 182 mHandlerThread = new HandlerThread("NSPHandlerThread"); 183 mHandlerThread.start(); 184 return mHandlerThread.getLooper(); 185 } 186 187 /** 188 * Constructor 189 * 190 * @param slotIndex SIM slot id the data service provider associated with. 191 */ IwlanNetworkServiceProvider(int slotIndex, IwlanNetworkService iwlanNetworkService)192 public IwlanNetworkServiceProvider(int slotIndex, IwlanNetworkService iwlanNetworkService) { 193 super(slotIndex); 194 SUB_TAG = TAG + "[" + slotIndex + "]"; 195 mIwlanNetworkService = iwlanNetworkService; 196 197 // Register IwlanEventListener 198 initHandler(); 199 List<Integer> events = new ArrayList<Integer>(); 200 events.add(IwlanEventListener.CROSS_SIM_CALLING_ENABLE_EVENT); 201 events.add(IwlanEventListener.CROSS_SIM_CALLING_DISABLE_EVENT); 202 IwlanEventListener.getInstance(mContext, slotIndex).addEventListener(events, mHandler); 203 } 204 initHandler()205 void initHandler() { 206 mHandler = new NSPHandler(getLooper()); 207 } 208 209 @Override requestNetworkRegistrationInfo(int domain, NetworkServiceCallback callback)210 public void requestNetworkRegistrationInfo(int domain, NetworkServiceCallback callback) { 211 if (callback == null) { 212 Log.d(SUB_TAG, "Error: callback is null. returning"); 213 return; 214 } 215 if (domain != NetworkRegistrationInfo.DOMAIN_PS) { 216 callback.onRequestNetworkRegistrationInfoComplete( 217 NetworkServiceCallback.RESULT_ERROR_UNSUPPORTED, null); 218 return; 219 } 220 221 NetworkRegistrationInfo.Builder nriBuilder = new NetworkRegistrationInfo.Builder(); 222 nriBuilder 223 .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN) 224 .setAvailableServices(Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA)) 225 .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN) 226 .setEmergencyOnly(!mIsSubActive) 227 .setDomain(NetworkRegistrationInfo.DOMAIN_PS); 228 229 if (!IwlanNetworkService.isNetworkConnected( 230 IwlanHelper.isDefaultDataSlot(mContext, getSlotIndex()), 231 IwlanHelper.isCrossSimCallingEnabled(mContext, getSlotIndex()))) { 232 nriBuilder.setRegistrationState( 233 NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING); 234 Log.d(SUB_TAG, "reg state REGISTRATION_STATE_NOT_REGISTERED_SEARCHING"); 235 } else { 236 nriBuilder.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME); 237 Log.d(SUB_TAG, "reg state REGISTRATION_STATE_HOME"); 238 } 239 240 callback.onRequestNetworkRegistrationInfoComplete( 241 NetworkServiceCallback.RESULT_SUCCESS, nriBuilder.build()); 242 } 243 244 /** 245 * Called when the instance of network service is destroyed (e.g. got unbind or binder died) 246 * or when the network service provider is removed. The extended class should implement this 247 * method to perform cleanup works. 248 */ 249 @Override close()250 public void close() { 251 mIwlanNetworkService.removeNetworkServiceProvider(this); 252 IwlanEventListener.getInstance(mContext, getSlotIndex()).removeEventListener(mHandler); 253 mHandlerThread.quit(); 254 } 255 256 @VisibleForTesting subscriptionChanged()257 void subscriptionChanged() { 258 boolean subActive = false; 259 SubscriptionManager sm = SubscriptionManager.from(mContext); 260 if (sm.getActiveSubscriptionInfoForSimSlotIndex(getSlotIndex()) != null) { 261 subActive = true; 262 } 263 if (subActive == mIsSubActive) { 264 return; 265 } 266 mIsSubActive = subActive; 267 if (subActive) { 268 Log.d(SUB_TAG, "sub changed from not_ready --> ready"); 269 } else { 270 Log.d(SUB_TAG, "sub changed from ready --> not_ready"); 271 } 272 273 notifyNetworkRegistrationInfoChanged(); 274 } 275 } 276 277 /** 278 * Create the instance of {@link NetworkServiceProvider}. Network service provider must override 279 * this method to facilitate the creation of {@link NetworkServiceProvider} instances. The 280 * system will call this method after binding the network service for each active SIM slot id. 281 * 282 * @param slotIndex SIM slot id the network service associated with. 283 * @return Network service object. Null if failed to create the provider (e.g. invalid slot 284 * index) 285 */ 286 @Override onCreateNetworkServiceProvider(int slotIndex)287 public NetworkServiceProvider onCreateNetworkServiceProvider(int slotIndex) { 288 Log.d(TAG, "onCreateNetworkServiceProvider: slotidx:" + slotIndex); 289 290 // TODO: validity check slot index 291 292 if (sIwlanNetworkServiceProviderList.isEmpty()) { 293 // first invocation 294 mNetworkCallbackHandlerThread = 295 new HandlerThread(IwlanNetworkService.class.getSimpleName()); 296 mNetworkCallbackHandlerThread.start(); 297 Looper looper = mNetworkCallbackHandlerThread.getLooper(); 298 Handler handler = new Handler(looper); 299 300 // register for default network callback 301 ConnectivityManager connectivityManager = 302 mContext.getSystemService(ConnectivityManager.class); 303 mNetworkMonitorCallback = new IwlanNetworkMonitorCallback(); 304 connectivityManager.registerDefaultNetworkCallback(mNetworkMonitorCallback, handler); 305 Log.d(TAG, "Registered with Connectivity Service"); 306 307 /* register with subscription manager */ 308 SubscriptionManager subscriptionManager = 309 mContext.getSystemService(SubscriptionManager.class); 310 mSubsChangeListener = new IwlanOnSubscriptionsChangedListener(); 311 subscriptionManager.addOnSubscriptionsChangedListener(mSubsChangeListener); 312 Log.d(TAG, "Registered with Subscription Service"); 313 } 314 315 IwlanNetworkServiceProvider np = new IwlanNetworkServiceProvider(slotIndex, this); 316 sIwlanNetworkServiceProviderList.add(np); 317 return np; 318 } 319 isNetworkConnected(boolean isDds, boolean isCstEnabled)320 public static boolean isNetworkConnected(boolean isDds, boolean isCstEnabled) { 321 if (!isDds && isCstEnabled) { 322 // Only Non-DDS sub with CST enabled, can use any transport. 323 return sNetworkConnected; 324 } else { 325 // For all other cases, only wifi transport can be used. 326 return ((sDefaultDataTransport == Transport.WIFI) && sNetworkConnected); 327 } 328 } 329 setNetworkConnected(boolean connected, Transport transport)330 public static void setNetworkConnected(boolean connected, Transport transport) { 331 if (connected == sNetworkConnected && transport == sDefaultDataTransport) { 332 return; 333 } 334 if (connected && (transport == IwlanNetworkService.Transport.UNSPECIFIED_NETWORK)) { 335 return; 336 } 337 sNetworkConnected = connected; 338 sDefaultDataTransport = transport; 339 340 for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) { 341 np.notifyNetworkRegistrationInfoChanged(); 342 } 343 } 344 removeNetworkServiceProvider(IwlanNetworkServiceProvider np)345 public void removeNetworkServiceProvider(IwlanNetworkServiceProvider np) { 346 sIwlanNetworkServiceProviderList.remove(np); 347 if (sIwlanNetworkServiceProviderList.isEmpty()) { 348 // deinit network related stuff 349 ConnectivityManager connectivityManager = 350 mContext.getSystemService(ConnectivityManager.class); 351 connectivityManager.unregisterNetworkCallback(mNetworkMonitorCallback); 352 mNetworkCallbackHandlerThread.quit(); // no need to quitSafely 353 mNetworkCallbackHandlerThread = null; 354 mNetworkMonitorCallback = null; 355 356 // deinit subscription manager related stuff 357 SubscriptionManager subscriptionManager = 358 mContext.getSystemService(SubscriptionManager.class); 359 subscriptionManager.removeOnSubscriptionsChangedListener(mSubsChangeListener); 360 mSubsChangeListener = null; 361 } 362 } 363 getContext()364 Context getContext() { 365 return getApplicationContext(); 366 } 367 368 @VisibleForTesting setAppContext(Context appContext)369 void setAppContext(Context appContext) { 370 mContext = appContext; 371 } 372 373 @VisibleForTesting getNetworkServiceProvider(int slotIndex)374 IwlanNetworkServiceProvider getNetworkServiceProvider(int slotIndex) { 375 for (IwlanNetworkServiceProvider np : sIwlanNetworkServiceProviderList) { 376 if (np.getSlotIndex() == slotIndex) { 377 return np; 378 } 379 } 380 return null; 381 } 382 383 @Override onCreate()384 public void onCreate() { 385 mContext = getApplicationContext(); 386 } 387 388 @Override onBind(Intent intent)389 public IBinder onBind(Intent intent) { 390 Log.d(TAG, "IwlanNetworkService onBind"); 391 return super.onBind(intent); 392 } 393 } 394