1 /* 2 * Copyright (C) 2019 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; 18 19 import android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.Network; 22 import android.net.NetworkCapabilities; 23 import android.net.NetworkRequest; 24 import android.os.Handler; 25 import android.telephony.SubscriptionManager; 26 import android.util.Log; 27 28 import com.android.internal.telephony.metrics.TelephonyMetrics; 29 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent; 30 31 /** 32 * This class will validate whether cellular network verified by Connectivity's 33 * validation process. It listens request on a specific subId, sends a network request 34 * to Connectivity and listens to its callback or timeout. 35 */ 36 public class CellularNetworkValidator { 37 private static final String LOG_TAG = "NetworkValidator"; 38 39 // States of validator. Only one validation can happen at once. 40 // IDLE: no validation going on. 41 private static final int STATE_IDLE = 0; 42 // VALIDATING: validation going on. 43 private static final int STATE_VALIDATING = 1; 44 // VALIDATED: validation is done and successful. 45 // Waiting for stopValidation() to release 46 // validationg NetworkRequest. 47 private static final int STATE_VALIDATED = 2; 48 49 // Singleton instance. 50 private static CellularNetworkValidator sInstance; 51 52 private int mState = STATE_IDLE; 53 private int mSubId; 54 private int mTimeoutInMs; 55 private boolean mReleaseAfterValidation; 56 57 private NetworkRequest mNetworkRequest; 58 private ValidationCallback mValidationCallback; 59 private Context mContext; 60 private ConnectivityManager mConnectivityManager; 61 private Handler mHandler = new Handler(); 62 private ConnectivityNetworkCallback mNetworkCallback; 63 64 /** 65 * Callback to pass in when starting validation. 66 */ 67 public interface ValidationCallback { 68 /** 69 * Validation failed, passed or timed out. 70 */ onValidationResult(boolean validated, int subId)71 void onValidationResult(boolean validated, int subId); 72 } 73 74 /** 75 * Create instance. 76 */ make(Context context)77 public static CellularNetworkValidator make(Context context) { 78 if (sInstance != null) { 79 logd("createCellularNetworkValidator failed. Instance already exists."); 80 } else { 81 sInstance = new CellularNetworkValidator(context); 82 } 83 84 return sInstance; 85 } 86 87 /** 88 * Get instance. 89 */ getInstance()90 public static CellularNetworkValidator getInstance() { 91 return sInstance; 92 } 93 94 /** 95 * Check whether this feature is supported or not. 96 */ isValidationFeatureSupported()97 public boolean isValidationFeatureSupported() { 98 return PhoneConfigurationManager.getInstance().getCurrentPhoneCapability() 99 .validationBeforeSwitchSupported; 100 } 101 CellularNetworkValidator(Context context)102 private CellularNetworkValidator(Context context) { 103 mContext = context; 104 mConnectivityManager = (ConnectivityManager) 105 mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 106 } 107 108 /** 109 * API to start a validation 110 */ validate(int subId, int timeoutInMs, boolean releaseAfterValidation, ValidationCallback callback)111 public synchronized void validate(int subId, int timeoutInMs, 112 boolean releaseAfterValidation, ValidationCallback callback) { 113 // If it's already validating the same subscription, do nothing. 114 if (subId == mSubId) return; 115 116 Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 117 if (phone == null) { 118 logd("Failed to start validation. Inactive subId " + subId); 119 callback.onValidationResult(false, subId); 120 return; 121 } 122 123 if (isValidating()) { 124 stopValidation(); 125 } 126 127 mState = STATE_VALIDATING; 128 mSubId = subId; 129 mTimeoutInMs = timeoutInMs; 130 mValidationCallback = callback; 131 mReleaseAfterValidation = releaseAfterValidation; 132 mNetworkRequest = createNetworkRequest(); 133 134 logd("Start validating subId " + mSubId + " mTimeoutInMs " + mTimeoutInMs 135 + " mReleaseAfterValidation " + mReleaseAfterValidation); 136 137 mNetworkCallback = new ConnectivityNetworkCallback(subId); 138 139 mConnectivityManager.requestNetwork( 140 mNetworkRequest, mNetworkCallback, mHandler, mTimeoutInMs); 141 } 142 143 /** 144 * API to stop the current validation. 145 */ stopValidation()146 public synchronized void stopValidation() { 147 if (!isValidating()) { 148 logd("No need to stop validation."); 149 } else { 150 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 151 mState = STATE_IDLE; 152 } 153 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 154 } 155 156 /** 157 * Return which subscription is under validating. 158 */ getSubIdInValidation()159 public synchronized int getSubIdInValidation() { 160 return mSubId; 161 } 162 163 /** 164 * Return whether there's an ongoing validation. 165 */ isValidating()166 public synchronized boolean isValidating() { 167 return mState != STATE_IDLE; 168 } 169 createNetworkRequest()170 private NetworkRequest createNetworkRequest() { 171 return new NetworkRequest.Builder() 172 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 173 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 174 .setNetworkSpecifier(String.valueOf(mSubId)) 175 .build(); 176 } 177 reportValidationResult(boolean passed, int subId)178 private synchronized void reportValidationResult(boolean passed, int subId) { 179 // If the validation result is not for current subId, do nothing. 180 if (mSubId != subId) return; 181 182 // Deal with the result only when state is still VALIDATING. This is to avoid 183 // receiving multiple callbacks in queue. 184 if (mState == STATE_VALIDATING) { 185 mValidationCallback.onValidationResult(passed, mSubId); 186 if (!mReleaseAfterValidation && passed) { 187 mState = STATE_VALIDATED; 188 } else { 189 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 190 mState = STATE_IDLE; 191 } 192 193 TelephonyMetrics.getInstance().writeNetworkValidate(passed 194 ? TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_PASSED 195 : TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_FAILED); 196 } 197 198 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 199 } 200 201 class ConnectivityNetworkCallback extends ConnectivityManager.NetworkCallback { 202 private final int mSubId; 203 ConnectivityNetworkCallback(int subId)204 ConnectivityNetworkCallback(int subId) { 205 mSubId = subId; 206 } 207 /** 208 * ConnectivityManager.NetworkCallback implementation 209 */ 210 @Override onAvailable(Network network)211 public void onAvailable(Network network) { 212 logd("network onAvailable " + network); 213 if (ConnectivityNetworkCallback.this.mSubId == CellularNetworkValidator.this.mSubId) { 214 TelephonyMetrics.getInstance().writeNetworkValidate( 215 TelephonyEvent.NetworkValidationState.NETWORK_VALIDATION_STATE_AVAILABLE); 216 } 217 } 218 219 @Override onLosing(Network network, int maxMsToLive)220 public void onLosing(Network network, int maxMsToLive) { 221 logd("network onLosing " + network + " maxMsToLive " + maxMsToLive); 222 reportValidationResult(false, ConnectivityNetworkCallback.this.mSubId); 223 } 224 225 @Override onLost(Network network)226 public void onLost(Network network) { 227 logd("network onLost " + network); 228 reportValidationResult(false, ConnectivityNetworkCallback.this.mSubId); 229 } 230 231 @Override onUnavailable()232 public void onUnavailable() { 233 logd("onUnavailable"); 234 reportValidationResult(false, ConnectivityNetworkCallback.this.mSubId); 235 } 236 237 @Override onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)238 public void onCapabilitiesChanged(Network network, 239 NetworkCapabilities networkCapabilities) { 240 if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { 241 logd("onValidated"); 242 reportValidationResult(true, ConnectivityNetworkCallback.this.mSubId); 243 } 244 } 245 } 246 logd(String log)247 private static void logd(String log) { 248 Log.d(LOG_TAG, log); 249 } 250 } 251