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