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