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 import static android.os.PowerManager.PARTIAL_WAKE_LOCK; 20 21 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_CHILD; 22 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD; 23 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE; 24 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_DPD; 25 26 import android.content.Context; 27 import android.net.ipsec.ike.ChildSessionCallback; 28 import android.net.ipsec.ike.ChildSessionParams; 29 import android.os.PowerManager; 30 import android.os.PowerManager.WakeLock; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 34 import java.util.LinkedList; 35 36 /** 37 * IkeLocalRequestScheduler caches all local requests scheduled by an IKE Session and notify the IKE 38 * Session to process the request when it is allowed. 39 * 40 * <p>LocalRequestScheduler is running on the IkeSessionStateMachine thread. 41 */ 42 public final class IkeLocalRequestScheduler { 43 private static final String TAG = "IkeLocalRequestScheduler"; 44 45 @VisibleForTesting static final String LOCAL_REQUEST_WAKE_LOCK_TAG = "LocalRequestWakeLock"; 46 47 public static int SPI_NOT_INCLUDED = 0; 48 49 private final PowerManager mPowerManager; 50 51 private final LinkedList<LocalRequest> mRequestQueue = new LinkedList<>(); 52 53 private final IProcedureConsumer mConsumer; 54 55 /** 56 * Construct an instance of IkeLocalRequestScheduler 57 * 58 * @param consumer the interface to initiate new procedure. 59 */ IkeLocalRequestScheduler(IProcedureConsumer consumer, Context context)60 public IkeLocalRequestScheduler(IProcedureConsumer consumer, Context context) { 61 mConsumer = consumer; 62 mPowerManager = context.getSystemService(PowerManager.class); 63 } 64 65 /** Add a new local request to the queue. */ addRequest(LocalRequest request)66 public void addRequest(LocalRequest request) { 67 request.acquireWakeLock(mPowerManager); 68 mRequestQueue.offer(request); 69 } 70 71 /** Add a new local request to the front of the queue. */ addRequestAtFront(LocalRequest request)72 public void addRequestAtFront(LocalRequest request) { 73 request.acquireWakeLock(mPowerManager); 74 mRequestQueue.offerFirst(request); 75 } 76 77 /** 78 * Notifies the scheduler that the caller is ready for a new procedure 79 * 80 * <p>Synchronously triggers the call to onNewProcedureReady. 81 * 82 * @return whether or not a new procedure was scheduled. 83 */ readyForNextProcedure()84 public boolean readyForNextProcedure() { 85 if (!mRequestQueue.isEmpty()) { 86 mConsumer.onNewProcedureReady(mRequestQueue.poll()); 87 return true; 88 } 89 return false; 90 } 91 92 /** Release WakeLocks of all LocalRequests in the queue */ releaseAllLocalRequestWakeLocks()93 public void releaseAllLocalRequestWakeLocks() { 94 for (LocalRequest req : mRequestQueue) { 95 req.releaseWakeLock(); 96 } 97 mRequestQueue.clear(); 98 } 99 100 /** 101 * This class represents the common information of procedures that will be locally initiated. 102 */ 103 public abstract static class LocalRequest { 104 public final int procedureType; 105 private WakeLock mWakeLock; 106 LocalRequest(int type)107 LocalRequest(int type) { 108 validateTypeOrThrow(type); 109 procedureType = type; 110 } 111 112 /** 113 * Acquire a WakeLock for the LocalRequest. 114 * 115 * <p>This method will only be called from IkeLocalRequestScheduler#addRequest or 116 * IkeLocalRequestScheduler#addRequestAtFront 117 */ acquireWakeLock(PowerManager powerManager)118 private void acquireWakeLock(PowerManager powerManager) { 119 if (mWakeLock != null && mWakeLock.isHeld()) { 120 getIkeLog().wtf(TAG, "This LocalRequest already acquired a WakeLock"); 121 return; 122 } 123 124 mWakeLock = 125 powerManager.newWakeLock( 126 PARTIAL_WAKE_LOCK, 127 TAG + LOCAL_REQUEST_WAKE_LOCK_TAG + "_" + procedureType); 128 mWakeLock.setReferenceCounted(false); 129 mWakeLock.acquire(); 130 } 131 132 /** Release WakeLock of the LocalRequest */ releaseWakeLock()133 public void releaseWakeLock() { 134 if (mWakeLock != null) { 135 mWakeLock.release(); 136 mWakeLock = null; 137 } 138 } 139 validateTypeOrThrow(int type)140 protected abstract void validateTypeOrThrow(int type); 141 isChildRequest()142 protected abstract boolean isChildRequest(); 143 } 144 145 /** 146 * This class represents a user requested or internally scheduled IKE procedure that will be 147 * initiated locally. 148 */ 149 public static class IkeLocalRequest extends LocalRequest { 150 public long remoteSpi; 151 152 /** Schedule a request for the IKE Session */ IkeLocalRequest(int type)153 IkeLocalRequest(int type) { 154 this(type, SPI_NOT_INCLUDED); 155 } 156 157 /** Schedule a request for an IKE SA that is identified by the remoteIkeSpi */ IkeLocalRequest(int type, long remoteIkeSpi)158 IkeLocalRequest(int type, long remoteIkeSpi) { 159 super(type); 160 remoteSpi = remoteIkeSpi; 161 } 162 163 @Override validateTypeOrThrow(int type)164 protected void validateTypeOrThrow(int type) { 165 if (type >= CMD_LOCAL_REQUEST_CREATE_IKE && type <= CMD_LOCAL_REQUEST_DPD) return; 166 throw new IllegalArgumentException("Invalid IKE procedure type: " + type); 167 } 168 169 @Override isChildRequest()170 protected boolean isChildRequest() { 171 return false; 172 } 173 } 174 175 /** 176 * This class represents a user requested or internally scheduled Child procedure that will be 177 * initiated locally. 178 */ 179 public static class ChildLocalRequest extends LocalRequest { 180 public int remoteSpi; 181 public final ChildSessionCallback childSessionCallback; 182 public final ChildSessionParams childSessionParams; 183 184 /** Schedule a request for a Child Session that is identified by the childCallback */ ChildLocalRequest( int type, ChildSessionCallback childCallback, ChildSessionParams childParams)185 ChildLocalRequest( 186 int type, ChildSessionCallback childCallback, ChildSessionParams childParams) { 187 this(type, SPI_NOT_INCLUDED, childCallback, childParams); 188 } 189 190 /** Schedule a request for a Child SA that is identified by the remoteChildSpi */ ChildLocalRequest(int type, int remoteChildSpi)191 ChildLocalRequest(int type, int remoteChildSpi) { 192 this(type, remoteChildSpi, null /*childCallback*/, null /*childParams*/); 193 } 194 ChildLocalRequest( int type, int remoteChildSpi, ChildSessionCallback childCallback, ChildSessionParams childParams)195 private ChildLocalRequest( 196 int type, 197 int remoteChildSpi, 198 ChildSessionCallback childCallback, 199 ChildSessionParams childParams) { 200 super(type); 201 childSessionParams = childParams; 202 childSessionCallback = childCallback; 203 remoteSpi = remoteChildSpi; 204 } 205 206 @Override validateTypeOrThrow(int type)207 protected void validateTypeOrThrow(int type) { 208 if (type >= CMD_LOCAL_REQUEST_CREATE_CHILD && type <= CMD_LOCAL_REQUEST_REKEY_CHILD) { 209 return; 210 } 211 212 throw new IllegalArgumentException("Invalid Child procedure type: " + type); 213 } 214 215 @Override isChildRequest()216 protected boolean isChildRequest() { 217 return true; 218 } 219 } 220 221 /** Interface to initiate a new IKE procedure */ 222 public interface IProcedureConsumer { 223 /** 224 * Called when a new IKE procedure can be initiated. 225 * 226 * @param localRequest the request to be initiated. 227 */ onNewProcedureReady(LocalRequest localRequest)228 void onNewProcedureReady(LocalRequest localRequest); 229 } 230 } 231