1/* 2 * Copyright (C) 2014 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 17package com.android.omadm.service; 18 19import android.app.Service; 20import android.content.Context; 21import android.content.Intent; 22import android.net.ConnectivityManager; 23import android.net.Network; 24import android.net.NetworkCapabilities; 25import android.net.NetworkInfo; 26import android.net.NetworkRequest; 27import android.os.IBinder; 28import android.util.Log; 29 30/** 31 * This service monitors the data connection and call state and brings up the FOTA APN when 32 * started by an {@link DMIntent#ACTION_START_DATA_CONNECTION_SERVICE} intent. 33 * 34 * TODO: handle operators which disallow OMA DM sessions over Wi-Fi. 35 * TODO: handle mobile data disabled and roaming disabled cases. 36 */ 37public class DMDataConnectionService extends Service { 38 private static final String TAG = "DMDataConnectionService"; 39 private static final boolean DBG = true; 40 41 private ConnectivityManager mConnectivityManager; 42 43 /** Start ID of current network request. */ 44 private int mCurrentStartId; 45 46 /** The active NetworkCallback for the FOTA APN, or null. */ 47 private ConnectivityManager.NetworkCallback mCellNetworkCallback; 48 49 /** The active NetworkCallback for WiFi or Ethernet, or null. */ 50 private ConnectivityManager.NetworkCallback mWiFiNetworkCallback; 51 52 /** The active FOTA APN network, or null. */ 53 private Network mActiveCellNetwork; 54 55 /** The active WiFi or Ethernet network, or null. */ 56 private Network mActiveWiFiNetwork; 57 58 /** Wait for 120 seconds after requesting the desired network types. */ 59 private static final int WAIT_FOR_NETWORK_TIMEOUT_MS = 120 * 1000; 60 61 @Override 62 public void onCreate() { 63 if (DBG) logd("onCreate()"); 64 mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 65 } 66 67 @Override 68 public void onDestroy() { 69 if (DBG) logd("onDestroy()"); 70 71 mCurrentStartId = 0; 72 73 if (mCellNetworkCallback != null) { 74 mConnectivityManager.unregisterNetworkCallback(mCellNetworkCallback); 75 if (DBG) logd("unregistered cell network callback"); 76 mCellNetworkCallback = null; 77 } 78 79 if (mWiFiNetworkCallback != null) { 80 mConnectivityManager.unregisterNetworkCallback(mWiFiNetworkCallback); 81 if (DBG) logd("unregistered WiFi network callback"); 82 mWiFiNetworkCallback = null; 83 } 84 // Unbind from any network. 85 ConnectivityManager.setProcessDefaultNetwork(null); 86 } 87 88 private boolean isWifiConnected() { 89 NetworkInfo ni = mConnectivityManager.getActiveNetworkInfo(); 90 return ni != null && ni.isConnected() 91 && (ni.getType() == ConnectivityManager.TYPE_WIFI 92 || ni.getType() == ConnectivityManager.TYPE_ETHERNET); 93 } 94 95 /** 96 * Request route using FOTA APN on the cell network and/or Wi-Fi or Ethernet transport. 97 * @param wifiOrEthernet true if Wi-Fi transport type is allowed; false for mobile data only 98 */ 99 private void requestNetworks(boolean wifiOrEthernet) { 100 if (wifiOrEthernet && isWifiConnected()) { 101 if (DBG) logd("requesting WiFi or Ethernet network transport"); 102 NetworkRequest request = new NetworkRequest.Builder() 103 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 104 .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET).build(); 105 106 mWiFiNetworkCallback = new WiFiCallback(); 107 mConnectivityManager.requestNetwork(request, mWiFiNetworkCallback, 108 WAIT_FOR_NETWORK_TIMEOUT_MS); 109 } else { 110 if (DBG) logd("requesting cell network with FOTA capability"); 111 NetworkRequest request = new NetworkRequest.Builder() 112 .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA).build(); 113 114 mCellNetworkCallback = new CellCallback(); 115 mConnectivityManager.requestNetwork(request, mCellNetworkCallback, 116 WAIT_FOR_NETWORK_TIMEOUT_MS); 117 } 118 } 119 120 @Override 121 public int onStartCommand(Intent intent, int flags, int startId) { 122 if (DBG) logd("onStartCommand: startID " + startId); 123 124 if (intent != null) { 125 String action = intent.getAction(); 126 if (DMIntent.ACTION_START_DATA_CONNECTION_SERVICE.equals(action)) { 127 int lastStartId = mCurrentStartId; 128 mCurrentStartId = startId; 129 130 if (lastStartId == 0) { 131 requestNetworks(true); // request FOTA APN or Wi-Fi/Ethernet networks 132 } 133 } else { 134 if (DBG) loge("unexpected intent: " + action); 135 stopSelf(startId); 136 } 137 } else { 138 if (DBG) logd("unexpected null intent"); 139 stopSelf(startId); 140 } 141 return Service.START_REDELIVER_INTENT; 142 } 143 144 private void sendDataConnectionReady() { 145 Intent intent = new Intent(this, DMIntentReceiver.class); 146 intent.setAction(DMIntent.ACTION_DATA_CONNECTION_READY); 147 sendBroadcast(intent); 148 } 149 150 // Callback for FOTA APN of cellular network. 151 private class CellCallback extends ConnectivityManager.NetworkCallback { 152 @Override 153 public void onAvailable(Network network) { 154 if (mCurrentStartId != 0) { 155 if (DBG) logd("CellCallback.onAvailable() for network: " + network); 156 mActiveCellNetwork = network; 157 if (mActiveWiFiNetwork == null) { 158 if (DBG) logd("calling setProcessDefaultNetwork() for cell network"); 159 ConnectivityManager.setProcessDefaultNetwork(network); 160 } 161 sendDataConnectionReady(); 162 } else { 163 if (DBG) loge("CellCallback: ignoring onAvailable() after service quit"); 164 } 165 } 166 167 @Override 168 public void onUnavailable() { 169 if (mCurrentStartId != 0) { 170 if (DBG) loge("CellCallback.onUnavailable() called (timeout)"); 171 mActiveCellNetwork = null; 172 if (mActiveWiFiNetwork == null) { 173 if (DBG) logd("clearing setProcessDefaultNetwork() binding"); 174 ConnectivityManager.setProcessDefaultNetwork(null); 175 } 176 } else { 177 if (DBG) loge("CellCallback: ignoring onUnavailable() after service quit"); 178 } 179 } 180 } 181 182 // Callback for WiFi connectivity. 183 private class WiFiCallback extends ConnectivityManager.NetworkCallback { 184 @Override 185 public void onAvailable(Network network) { 186 if (mCurrentStartId != 0) { 187 if (DBG) logd("WiFiCallback.onAvailable() for network: " + network); 188 mActiveWiFiNetwork = network; 189 ConnectivityManager.setProcessDefaultNetwork(network); 190 sendDataConnectionReady(); 191 } else { 192 if (DBG) loge("WiFiCallback: ignoring onAvailable() after service quit"); 193 } 194 } 195 196 @Override 197 public void onUnavailable() { 198 if (mCurrentStartId != 0) { 199 if (DBG) loge("WiFi.onUnavailable() called (timeout)"); 200 mActiveWiFiNetwork = null; 201 if (mActiveCellNetwork == null) { 202 if (DBG) logd("clearing setProcessDefaultNetwork() binding"); 203 ConnectivityManager.setProcessDefaultNetwork(null); 204 requestNetworks(false); // request FOTA APN only 205 } 206 } else { 207 if (DBG) loge("WiFiCallback: ignoring onUnavailable() after service quit"); 208 } 209 } 210 } 211 212 @Override 213 public IBinder onBind(Intent intent) { 214 return null; 215 } 216 217 private static void logd(String msg) { 218 Log.d(TAG, msg); 219 } 220 221 private static void loge(String msg) { 222 Log.e(TAG, msg); 223 } 224} 225