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 package com.android.internal.net.ipsec.ike; 17 18 import static android.net.ipsec.ike.IkeManager.getIkeLog; 19 20 import android.os.Looper; 21 import android.os.Message; 22 import android.util.SparseArray; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.internal.util.IState; 26 import com.android.internal.util.State; 27 import com.android.internal.util.StateMachine; 28 29 import java.util.concurrent.Executor; 30 import java.util.concurrent.TimeUnit; 31 32 /** 33 * This class represents the common information of both IkeSessionStateMachine and 34 * ChildSessionStateMachine 35 */ 36 abstract class AbstractSessionStateMachine extends StateMachine { 37 private static final int CMD_SHARED_BASE = 0; 38 protected static final int CMD_CATEGORY_SIZE = 100; 39 40 /** 41 * Commands of Child local request that will be used in both IkeSessionStateMachine and 42 * ChildSessionStateMachine. 43 */ 44 protected static final int CMD_CHILD_LOCAL_REQUEST_BASE = CMD_SHARED_BASE; 45 46 @VisibleForTesting 47 static final int CMD_LOCAL_REQUEST_CREATE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 1; 48 49 @VisibleForTesting 50 static final int CMD_LOCAL_REQUEST_DELETE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 2; 51 52 @VisibleForTesting 53 static final int CMD_LOCAL_REQUEST_REKEY_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 3; 54 55 @VisibleForTesting 56 static final int CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE = CMD_CHILD_LOCAL_REQUEST_BASE + 4; 57 58 /** Timeout commands. */ 59 protected static final int CMD_TIMEOUT_BASE = CMD_SHARED_BASE + CMD_CATEGORY_SIZE; 60 /** Timeout when the remote side fails to send a Rekey-Delete request. */ 61 @VisibleForTesting static final int TIMEOUT_REKEY_REMOTE_DELETE = CMD_TIMEOUT_BASE + 1; 62 63 /** Commands for generic usages */ 64 protected static final int CMD_GENERIC_BASE = CMD_SHARED_BASE + 2 * CMD_CATEGORY_SIZE; 65 /** Force state machine to a target state for testing purposes. */ 66 @VisibleForTesting static final int CMD_FORCE_TRANSITION = CMD_GENERIC_BASE + 1; 67 /** Force close the session. */ 68 @VisibleForTesting static final int CMD_KILL_SESSION = CMD_GENERIC_BASE + 2; 69 70 /** Private commands for subclasses */ 71 protected static final int CMD_PRIVATE_BASE = CMD_SHARED_BASE + 3 * CMD_CATEGORY_SIZE; 72 73 protected static final SparseArray<String> SHARED_CMD_TO_STR; 74 75 static { 76 SHARED_CMD_TO_STR = new SparseArray<>(); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_CHILD, "Create Child")77 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_CHILD, "Create Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_CHILD, "Delete Child")78 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_CHILD, "Delete Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD, "Rekey Child")79 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD, "Rekey Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE, "Rekey Child (MOBIKE)")80 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE, "Rekey Child (MOBIKE)"); SHARED_CMD_TO_STR.put(CMD_KILL_SESSION, "Kill session")81 SHARED_CMD_TO_STR.put(CMD_KILL_SESSION, "Kill session"); SHARED_CMD_TO_STR.put(TIMEOUT_REKEY_REMOTE_DELETE, "Timout rekey remote delete")82 SHARED_CMD_TO_STR.put(TIMEOUT_REKEY_REMOTE_DELETE, "Timout rekey remote delete"); SHARED_CMD_TO_STR.put(CMD_FORCE_TRANSITION, "Force transition")83 SHARED_CMD_TO_STR.put(CMD_FORCE_TRANSITION, "Force transition"); 84 } 85 86 // Use a value greater than the retransmit-failure timeout. 87 static final long REKEY_DELETE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(180L); 88 89 // Default delay time for retrying a request 90 static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L); 91 92 protected final Executor mUserCbExecutor; 93 private final String mLogTag; 94 95 protected volatile boolean mIsClosing = false; 96 AbstractSessionStateMachine(String name, Looper looper, Executor userCbExecutor)97 protected AbstractSessionStateMachine(String name, Looper looper, Executor userCbExecutor) { 98 super(name, looper); 99 mLogTag = name; 100 mUserCbExecutor = userCbExecutor; 101 } 102 103 /** 104 * Top level state for handling uncaught exceptions for all subclasses. 105 * 106 * <p>All other state in SessionStateMachine MUST extend this state. 107 * 108 * <p>Only errors this state should catch are unexpected internal failures. Since this may be 109 * run in critical processes, it must never take down the process if it fails 110 */ 111 protected abstract class ExceptionHandlerBase extends State { 112 @Override enter()113 public final void enter() { 114 try { 115 enterState(); 116 } catch (RuntimeException e) { 117 cleanUpAndQuit(e); 118 } 119 } 120 getCmdStr(int cmd)121 private String getCmdStr(int cmd) { 122 String cmdName = SHARED_CMD_TO_STR.get(cmd); 123 if (cmdName != null) { 124 return cmdName; 125 } 126 127 cmdName = getCmdString(cmd); 128 if (cmdName != null) { 129 return cmdName; 130 } 131 132 // Unrecognized message 133 return Integer.toString(cmd); 134 } 135 136 @Override processMessage(Message message)137 public final boolean processMessage(Message message) { 138 try { 139 if (mIsClosing && message.what != CMD_KILL_SESSION) { 140 logd( 141 "Ignore " 142 + getCmdStr(message.what) 143 + " since this session is going to be closed"); 144 return HANDLED; 145 } else { 146 logd("processStateMessage: " + getCmdStr(message.what)); 147 return processStateMessage(message); 148 } 149 } catch (RuntimeException e) { 150 cleanUpAndQuit(e); 151 return HANDLED; 152 } 153 } 154 155 @Override exit()156 public final void exit() { 157 try { 158 exitState(); 159 } catch (RuntimeException e) { 160 cleanUpAndQuit(e); 161 } 162 } 163 enterState()164 protected void enterState() { 165 // Do nothing. Subclasses MUST override it if they care. 166 } 167 processStateMessage(Message message)168 protected boolean processStateMessage(Message message) { 169 return NOT_HANDLED; 170 } 171 exitState()172 protected void exitState() { 173 // Do nothing. Subclasses MUST override it if they care. 174 } 175 cleanUpAndQuit(RuntimeException e)176 protected abstract void cleanUpAndQuit(RuntimeException e); 177 getCmdString(int cmd)178 protected abstract String getCmdString(int cmd); 179 } 180 executeUserCallback(Runnable r)181 protected void executeUserCallback(Runnable r) { 182 try { 183 mUserCbExecutor.execute(r); 184 } catch (Exception e) { 185 logd("Callback execution failed", e); 186 } 187 } 188 189 /** Forcibly close this session. */ killSession()190 public void killSession() { 191 log("killSession"); 192 193 mIsClosing = true; 194 sendMessage(CMD_KILL_SESSION); 195 } 196 197 /** 198 * Quit SessionStateMachine immediately. 199 * 200 * <p>This method pushes SM_QUIT_CMD in front of the message queue and mark mIsClosing as true. 201 * All currently queued messages will be discarded. 202 * 203 * <p>Subclasses MUST call this method instead of quitNow() 204 */ 205 // quitNow() is a public final method in the base class. Thus there is no good way to prevent 206 // caller from calling it within the current inheritance structure. quitSessionNow()207 protected void quitSessionNow() { 208 mIsClosing = true; 209 quitNow(); 210 } 211 getCurrentStateName()212 protected String getCurrentStateName() { 213 final IState state = getCurrentState(); 214 if (state != null) { 215 return state.getName(); 216 } 217 218 return "Null State"; 219 } 220 221 @Override log(String s)222 protected void log(String s) { 223 getIkeLog().d(mLogTag, s); 224 } 225 226 @Override logd(String s)227 protected void logd(String s) { 228 getIkeLog().d(mLogTag, s); 229 } 230 logd(String s, Throwable e)231 protected void logd(String s, Throwable e) { 232 getIkeLog().d(mLogTag, s, e); 233 } 234 235 @Override logv(String s)236 protected void logv(String s) { 237 getIkeLog().v(mLogTag, s); 238 } 239 240 @Override logi(String s)241 protected void logi(String s) { 242 getIkeLog().i(mLogTag, s); 243 } 244 logi(String s, Throwable cause)245 protected void logi(String s, Throwable cause) { 246 getIkeLog().i(mLogTag, s, cause); 247 } 248 249 @Override logw(String s)250 protected void logw(String s) { 251 getIkeLog().w(mLogTag, s); 252 } 253 254 @Override loge(String s)255 protected void loge(String s) { 256 getIkeLog().e(mLogTag, s); 257 } 258 259 @Override loge(String s, Throwable e)260 protected void loge(String s, Throwable e) { 261 getIkeLog().e(mLogTag, s, e); 262 } 263 logWtf(String s)264 protected void logWtf(String s) { 265 getIkeLog().wtf(mLogTag, s); 266 } 267 logWtf(String s, Throwable e)268 protected void logWtf(String s, Throwable e) { 269 getIkeLog().wtf(mLogTag, s, e); 270 } 271 } 272