1 /* 2 * Copyright (C) 2023 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 package com.android.internal.net.ipsec.ike.utils; 17 18 import static android.net.ipsec.ike.IkeManager.getIkeLog; 19 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.net.ipsec.ike.IkeSessionCallback; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.util.concurrent.Executor; 27 28 /** 29 * The LivenessAssister helps the process from requesting liveness check to delivering a result, and 30 * helps to handle status callbacks according to the results. 31 * 32 * <p>The liveness check has a simple process of receiving a request from a client, checking a 33 * peer's liveness, and then reporting the results. Results can only be reported upon request, and 34 * the LivenessAssister also provides callbacks for the results. 35 * 36 * <p>A process of state change as follows. Failure will be reported before closing the session. 37 * 38 * <ul> 39 * <li>Requested -> report (on-demand or background) Started -> report Success -> Clear Requested 40 * <li>Requested -> report (on-demand or background) Started -> report failure -> no longer used 41 * <li>Requested -> report (on-demand) Started -> if requested again -> report (on-demand) ongoing 42 * -> report Success -> Clear Requested 43 * <li>Requested -> report (on-demand) Started -> if requested again -> report (on-demand) ongoing 44 * -> report failure -> no longer used 45 * <li>Requested -> report (background) Started -> if requested again -> report (background) 46 * ongoing -> report Success -> NotRequested 47 * <li>Requested -> report (background) Started -> if requested again -> report (background) 48 * ongoing -> report failure -> no longer used 49 * </ul> 50 */ 51 public class LivenessAssister { 52 53 private static final String TAG = LivenessAssister.class.getSimpleName(); 54 55 /** Initial. */ 56 public static final int REQ_TYPE_INITIAL = 0; 57 58 /** A liveness check request is performed as an on-demand task. */ 59 public static final int REQ_TYPE_ON_DEMAND = 1; 60 61 /** A liveness check request is performed in the background. */ 62 public static final int REQ_TYPE_BACKGROUND = 2; 63 64 @Retention(RetentionPolicy.SOURCE) 65 @IntDef({ 66 REQ_TYPE_INITIAL, 67 REQ_TYPE_ON_DEMAND, 68 REQ_TYPE_BACKGROUND, 69 }) 70 @interface LivenessRequestType {} 71 72 private final IkeSessionCallback mCallback; 73 private final Executor mUserCbExecutor; 74 75 private int mLivenessCheckRequested; 76 77 private LivenessMetricHelper mLivenessMetricHelper; 78 LivenessAssister( @onNull IkeSessionCallback callback, @NonNull Executor executor, @NonNull IIkeMetricsCallback metricsCallback)79 public LivenessAssister( 80 @NonNull IkeSessionCallback callback, 81 @NonNull Executor executor, 82 @NonNull IIkeMetricsCallback metricsCallback) { 83 mCallback = callback; 84 mUserCbExecutor = executor; 85 mLivenessCheckRequested = REQ_TYPE_INITIAL; 86 mLivenessMetricHelper = new LivenessMetricHelper(metricsCallback); 87 } 88 89 /** 90 * Set the status as requested for the liveness check and notify the associated liveness status. 91 * 92 * <p>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_ON_DEMAND_STARTED}: This status 93 * is notified when liveness checking is started with a new on-demand DPD task. 94 * 95 * <p>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_ON_DEMAND_ONGOING}: This status 96 * is notified when liveness checking is already running in an on-demand DPD task. 97 * 98 * <p>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_BACKGROUND_STARTED}: This status 99 * is notified when liveness checking is started with joining an existing running task. 100 * 101 * <p>{@link IkeSessionCallback.LivenessStatus#LIVENESS_STATUS_BACKGROUND_ONGOING}: This status 102 * is notified when liveness checking is already running with joining an existing running task. 103 * 104 * @param requestType request type of {@link LivenessRequestType#REQ_TYPE_ON_DEMAND} or {@link 105 * LivenessRequestType#REQ_TYPE_BACKGROUND} 106 */ livenessCheckRequested(@ivenessRequestType int requestType)107 public void livenessCheckRequested(@LivenessRequestType int requestType) { 108 switch (mLivenessCheckRequested) { 109 case REQ_TYPE_INITIAL: 110 if (requestType == REQ_TYPE_ON_DEMAND) { 111 mLivenessCheckRequested = REQ_TYPE_ON_DEMAND; 112 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_STARTED); 113 } else if (requestType == REQ_TYPE_BACKGROUND) { 114 mLivenessCheckRequested = REQ_TYPE_BACKGROUND; 115 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_STARTED); 116 } 117 break; 118 case REQ_TYPE_ON_DEMAND: 119 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_ONGOING); 120 break; 121 case REQ_TYPE_BACKGROUND: 122 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_ONGOING); 123 break; 124 } 125 } 126 127 /** 128 * Mark that the liveness check was successful. 129 * 130 * <p>and notifies a {@link IkeSessionCallback#LIVENESS_STATUS_SUCCESS}. 131 */ markPeerAsAlive()132 public void markPeerAsAlive() { 133 if (mLivenessCheckRequested == REQ_TYPE_INITIAL) { 134 return; 135 } 136 137 mLivenessCheckRequested = REQ_TYPE_INITIAL; 138 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_SUCCESS); 139 } 140 141 /** 142 * Mark that the liveness check was failed. 143 * 144 * <p>and notifies a {@link IkeSessionCallback#LIVENESS_STATUS_FAILURE}. 145 */ markPeerAsDead()146 public void markPeerAsDead() { 147 if (mLivenessCheckRequested == REQ_TYPE_INITIAL) { 148 return; 149 } 150 151 mLivenessCheckRequested = REQ_TYPE_INITIAL; 152 invokeUserCallback(IkeSessionCallback.LIVENESS_STATUS_FAILURE); 153 } 154 155 /** 156 * Returns whether the request has been received and the results have not yet been reported. 157 * 158 * @return {@code true} if the liveness check has been requested. 159 */ isLivenessCheckRequested()160 public boolean isLivenessCheckRequested() { 161 return mLivenessCheckRequested != REQ_TYPE_INITIAL; 162 } 163 164 /** 165 * Callbacks liveness status to clients. 166 * 167 * @param status {@link IkeSessionCallback.LivenessStatus} to be notified. 168 */ invokeUserCallback(@keSessionCallback.LivenessStatus int status)169 private void invokeUserCallback(@IkeSessionCallback.LivenessStatus int status) { 170 try { 171 mUserCbExecutor.execute(() -> mCallback.onLivenessStatusChanged(status)); 172 mLivenessMetricHelper.recordLivenessStatus(status); 173 } catch (Exception e) { 174 getIkeLog().e(TAG, "onLivenessStatusChanged execution failed", e); 175 } 176 } 177 178 /** Interface for receiving values that make up atoms */ 179 public interface IIkeMetricsCallback { 180 /** Notifies that the liveness check has been completed. */ onLivenessCheckCompleted( int elapsedTimeInMillis, int numberOfOnGoing, boolean resultSuccess)181 void onLivenessCheckCompleted( 182 int elapsedTimeInMillis, int numberOfOnGoing, boolean resultSuccess); 183 } 184 185 private static class LivenessMetricHelper { 186 187 /** To log metric information, call the function when ready to send it. */ 188 private final IIkeMetricsCallback mMetricsCallback; 189 190 private long mTimeInMillisStartedStatus; 191 private int mNumberOfOnGoing; 192 LivenessMetricHelper(IIkeMetricsCallback metricsCallback)193 LivenessMetricHelper(IIkeMetricsCallback metricsCallback) { 194 clearVariables(); 195 mMetricsCallback = metricsCallback; 196 } 197 clearVariables()198 private void clearVariables() { 199 mTimeInMillisStartedStatus = 0L; 200 mNumberOfOnGoing = 0; 201 } 202 recordLivenessStatus(@keSessionCallback.LivenessStatus int status)203 public void recordLivenessStatus(@IkeSessionCallback.LivenessStatus int status) { 204 switch (status) { 205 case IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_STARTED: // fallthrough 206 case IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_STARTED: 207 clearVariables(); 208 mTimeInMillisStartedStatus = System.currentTimeMillis(); 209 break; 210 case IkeSessionCallback.LIVENESS_STATUS_ON_DEMAND_ONGOING: // fallthrough 211 case IkeSessionCallback.LIVENESS_STATUS_BACKGROUND_ONGOING: 212 mNumberOfOnGoing++; 213 break; 214 case IkeSessionCallback.LIVENESS_STATUS_SUCCESS: 215 onLivenessCheckCompleted(true); 216 break; 217 case IkeSessionCallback.LIVENESS_STATUS_FAILURE: 218 onLivenessCheckCompleted(false); 219 break; 220 } 221 } 222 onLivenessCheckCompleted(boolean resultSuccess)223 private void onLivenessCheckCompleted(boolean resultSuccess) { 224 long elapsedTimeInMillis = System.currentTimeMillis() - mTimeInMillisStartedStatus; 225 if (elapsedTimeInMillis < 0L || elapsedTimeInMillis > Integer.MAX_VALUE) { 226 getIkeLog() 227 .e( 228 TAG, 229 "onLivenessCheckCompleted, time exceeded failed. timeInMillies:" 230 + elapsedTimeInMillis); 231 clearVariables(); 232 return; 233 } 234 235 mMetricsCallback.onLivenessCheckCompleted( 236 (int) elapsedTimeInMillis, mNumberOfOnGoing, resultSuccess); 237 clearVariables(); 238 } 239 } 240 } 241