• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_DELETE_CHILD;
23 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_MAX;
24 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_MIGRATE_CHILD;
25 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_MIN;
26 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD;
27 import static com.android.internal.net.ipsec.ike.AbstractSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE;
28 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_CREATE_IKE;
29 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_DELETE_IKE;
30 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_DPD;
31 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_INFO;
32 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_MOBIKE;
33 import static com.android.internal.net.ipsec.ike.IkeSessionStateMachine.CMD_LOCAL_REQUEST_REKEY_IKE;
34 
35 import android.annotation.IntDef;
36 import android.content.Context;
37 import android.net.ipsec.ike.ChildSessionCallback;
38 import android.net.ipsec.ike.ChildSessionParams;
39 import android.os.PowerManager;
40 import android.os.PowerManager.WakeLock;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 
44 import java.lang.annotation.Retention;
45 import java.lang.annotation.RetentionPolicy;
46 import java.util.Comparator;
47 import java.util.PriorityQueue;
48 
49 /**
50  * IkeLocalRequestScheduler caches all local requests scheduled by an IKE Session and notify the IKE
51  * Session to process the request when it is allowed.
52  *
53  * <p>LocalRequestScheduler is running on the IkeSessionStateMachine thread.
54  */
55 public final class IkeLocalRequestScheduler {
56     private static final String TAG = "IkeLocalRequestScheduler";
57 
58     @VisibleForTesting static final String LOCAL_REQUEST_WAKE_LOCK_TAG = "LocalRequestWakeLock";
59 
60     private static final int DEFAULT_REQUEST_QUEUE_SIZE = 1;
61 
62     private static final int REQUEST_ID_NOT_ASSIGNED = -1;
63 
64     // Local request that must be handled immediately. Ex: CMD_LOCAL_REQUEST_DELETE_IKE
65     @VisibleForTesting static final int REQUEST_PRIORITY_URGENT = 0;
66 
67     // Local request that must be handled soon, but not necessarily immediately.
68     // Ex: CMD_LOCAL_REQUEST_MOBIKE
69     @VisibleForTesting static final int REQUEST_PRIORITY_HIGH = 1;
70 
71     // Local request that should be handled once nothing more urgent requires handling. Most
72     // LocalRequests will have this priority.
73     @VisibleForTesting static final int REQUEST_PRIORITY_NORMAL = 2;
74 
75     // Local request that has an unknown priority. This shouldn't happen in normal processing.
76     @VisibleForTesting static final int REQUEST_PRIORITY_UNKNOWN = Integer.MAX_VALUE;
77 
78     @Retention(RetentionPolicy.SOURCE)
79     @IntDef({
80         REQUEST_PRIORITY_URGENT,
81         REQUEST_PRIORITY_HIGH,
82         REQUEST_PRIORITY_NORMAL,
83         REQUEST_PRIORITY_UNKNOWN
84     })
85     @interface RequestPriority {}
86 
87     public static int SPI_NOT_INCLUDED = 0;
88 
89     private final PowerManager mPowerManager;
90 
91     private final PriorityQueue<LocalRequest> mRequestQueue =
92             new PriorityQueue<>(DEFAULT_REQUEST_QUEUE_SIZE, new LocalRequestComparator());
93 
94     private final IProcedureConsumer mConsumer;
95 
96     private int mNextRequestId;
97 
98     /**
99      * Construct an instance of IkeLocalRequestScheduler
100      *
101      * @param consumer the interface to initiate new procedure.
102      */
IkeLocalRequestScheduler(IProcedureConsumer consumer, Context context)103     public IkeLocalRequestScheduler(IProcedureConsumer consumer, Context context) {
104         mConsumer = consumer;
105         mPowerManager = context.getSystemService(PowerManager.class);
106 
107         mNextRequestId = 0;
108     }
109 
110     /** Add a new local request to the queue. */
addRequest(LocalRequest request)111     public void addRequest(LocalRequest request) {
112         request.acquireWakeLock(mPowerManager);
113         request.setRequestId(mNextRequestId++);
114         mRequestQueue.offer(request);
115     }
116 
117     /**
118      * Notifies the scheduler that the caller is ready for a new procedure
119      *
120      * <p>Synchronously triggers the call to onNewProcedureReady.
121      *
122      * @return whether or not a new procedure was scheduled.
123      */
readyForNextProcedure()124     public boolean readyForNextProcedure() {
125         if (!mRequestQueue.isEmpty()) {
126             mConsumer.onNewProcedureReady(mRequestQueue.poll());
127             return true;
128         }
129         return false;
130     }
131 
132     /** Release WakeLocks of all LocalRequests in the queue */
releaseAllLocalRequestWakeLocks()133     public void releaseAllLocalRequestWakeLocks() {
134         for (LocalRequest req : mRequestQueue) {
135             req.releaseWakeLock();
136         }
137         mRequestQueue.clear();
138     }
139 
140     /**
141      * This class represents the common information of procedures that will be locally initiated.
142      */
143     public abstract static class LocalRequest {
144         public final int procedureType;
145 
146         // Priority of this LocalRequest. Note that a lower 'priority' means higher urgency.
147         @RequestPriority private final int mPriority;
148 
149         // ID used to preserve insertion-order between requests in IkeLocalRequestScheduler with the
150         // same priority. Set when the LocalRequest is added to the IkeLocalRequestScheduler.
151         private int mRequestId = REQUEST_ID_NOT_ASSIGNED;
152         private WakeLock mWakeLock;
153 
LocalRequest(int type, int priority)154         LocalRequest(int type, int priority) {
155             validateTypeOrThrow(type);
156             procedureType = type;
157             mPriority = priority;
158         }
159 
160         @VisibleForTesting
getPriority()161         int getPriority() {
162             return mPriority;
163         }
164 
setRequestId(int requestId)165         private void setRequestId(int requestId) {
166             mRequestId = requestId;
167         }
168 
169         @VisibleForTesting
getRequestId()170         int getRequestId() {
171             return mRequestId;
172         }
173 
174         /**
175          * Acquire a WakeLock for the LocalRequest.
176          *
177          * <p>This method will only be called from IkeLocalRequestScheduler#addRequest or
178          * IkeLocalRequestScheduler#addRequestAtFront
179          */
acquireWakeLock(PowerManager powerManager)180         private void acquireWakeLock(PowerManager powerManager) {
181             if (mWakeLock != null && mWakeLock.isHeld()) {
182                 getIkeLog().wtf(TAG, "This LocalRequest already acquired a WakeLock");
183                 return;
184             }
185 
186             mWakeLock =
187                     powerManager.newWakeLock(
188                             PARTIAL_WAKE_LOCK,
189                             TAG + LOCAL_REQUEST_WAKE_LOCK_TAG + "_" + procedureType);
190             mWakeLock.setReferenceCounted(false);
191             mWakeLock.acquire();
192         }
193 
194         /** Release WakeLock of the LocalRequest */
releaseWakeLock()195         public void releaseWakeLock() {
196             if (mWakeLock != null) {
197                 mWakeLock.release();
198                 mWakeLock = null;
199             }
200         }
201 
validateTypeOrThrow(int type)202         protected abstract void validateTypeOrThrow(int type);
203 
isChildRequest()204         protected abstract boolean isChildRequest();
205     }
206 
207     /** LocalRequestComparator is a comparator for comparing LocalRequest instances. */
208     private class LocalRequestComparator implements Comparator<LocalRequest> {
209         @Override
compare(LocalRequest requestA, LocalRequest requestB)210         public int compare(LocalRequest requestA, LocalRequest requestB) {
211             int relativePriorities =
212                     Integer.compare(requestA.getPriority(), requestB.getPriority());
213             if (relativePriorities != 0) return relativePriorities;
214 
215             return Integer.compare(requestA.getRequestId(), requestB.getRequestId());
216         }
217     }
218 
219     /**
220      * This class represents a user requested or internally scheduled IKE procedure that will be
221      * initiated locally.
222      */
223     public static class IkeLocalRequest extends LocalRequest {
224         public long remoteSpi;
225 
226         /** Schedule a request for an IKE SA that is identified by the remoteIkeSpi */
IkeLocalRequest(int type, long remoteIkeSpi, int priority)227         private IkeLocalRequest(int type, long remoteIkeSpi, int priority) {
228             super(type, priority);
229             remoteSpi = remoteIkeSpi;
230         }
231 
232         @Override
validateTypeOrThrow(int type)233         protected void validateTypeOrThrow(int type) {
234             if (type >= CMD_LOCAL_REQUEST_CREATE_IKE && type <= CMD_LOCAL_REQUEST_MOBIKE) return;
235             throw new IllegalArgumentException("Invalid IKE procedure type: " + type);
236         }
237 
238         @Override
isChildRequest()239         protected boolean isChildRequest() {
240             return false;
241         }
242     }
243 
244     /**
245      * This class represents a user requested or internally scheduled Child procedure that will be
246      * initiated locally.
247      */
248     public static class ChildLocalRequest extends LocalRequest {
249         public int remoteSpi;
250         public final ChildSessionCallback childSessionCallback;
251         public final ChildSessionParams childSessionParams;
252 
ChildLocalRequest( int type, int remoteChildSpi, ChildSessionCallback childCallback, ChildSessionParams childParams, int priority)253         private ChildLocalRequest(
254                 int type,
255                 int remoteChildSpi,
256                 ChildSessionCallback childCallback,
257                 ChildSessionParams childParams,
258                 int priority) {
259             super(type, priority);
260             childSessionParams = childParams;
261             childSessionCallback = childCallback;
262             remoteSpi = remoteChildSpi;
263         }
264 
265         @Override
validateTypeOrThrow(int type)266         protected void validateTypeOrThrow(int type) {
267             if (type >= CMD_LOCAL_REQUEST_MIN && type <= CMD_LOCAL_REQUEST_MAX) {
268                 return;
269             }
270 
271             throw new IllegalArgumentException("Invalid Child procedure type: " + type);
272         }
273 
274         @Override
isChildRequest()275         protected boolean isChildRequest() {
276             return true;
277         }
278     }
279 
280     /** Interface to initiate a new IKE procedure */
281     public interface IProcedureConsumer {
282         /**
283          * Called when a new IKE procedure can be initiated.
284          *
285          * @param localRequest the request to be initiated.
286          */
onNewProcedureReady(LocalRequest localRequest)287         void onNewProcedureReady(LocalRequest localRequest);
288     }
289 
290     /** package-protected */
291     static class LocalRequestFactory {
292         /** Create a request for the IKE Session */
getIkeLocalRequest(int type)293         IkeLocalRequest getIkeLocalRequest(int type) {
294             return getIkeLocalRequest(type, SPI_NOT_INCLUDED);
295         }
296 
297         /** Create a request for an IKE SA that is identified by the remoteIkeSpi */
getIkeLocalRequest(int type, long remoteIkeSpi)298         IkeLocalRequest getIkeLocalRequest(int type, long remoteIkeSpi) {
299             return new IkeLocalRequest(type, remoteIkeSpi, procedureTypeToPriority(type));
300         }
301 
302         /** Create a request for a Child Session that is identified by the childCallback */
getChildLocalRequest( int type, ChildSessionCallback childCallback, ChildSessionParams childParams)303         ChildLocalRequest getChildLocalRequest(
304                 int type, ChildSessionCallback childCallback, ChildSessionParams childParams) {
305             return new ChildLocalRequest(
306                     type,
307                     SPI_NOT_INCLUDED,
308                     childCallback,
309                     childParams,
310                     procedureTypeToPriority(type));
311         }
312 
313         /** Create a request for a Child SA that is identified by the remoteChildSpi */
getChildLocalRequest(int type, int remoteChildSpi)314         ChildLocalRequest getChildLocalRequest(int type, int remoteChildSpi) {
315             return new ChildLocalRequest(
316                     type,
317                     remoteChildSpi,
318                     null /*childCallback*/,
319                     null /*childParams*/,
320                     procedureTypeToPriority(type));
321         }
322 
323         /** Returns the request priority for the specified procedure type. */
324         @VisibleForTesting
325         @RequestPriority
procedureTypeToPriority(int procedureType)326         static int procedureTypeToPriority(int procedureType) {
327             switch (procedureType) {
328                 case CMD_LOCAL_REQUEST_DELETE_IKE:
329                     return REQUEST_PRIORITY_URGENT;
330 
331                 case CMD_LOCAL_REQUEST_MOBIKE:
332                 case CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE:
333                 case CMD_LOCAL_REQUEST_MIGRATE_CHILD:
334                     return REQUEST_PRIORITY_HIGH;
335 
336                 case CMD_LOCAL_REQUEST_CREATE_IKE: // Fallthrough
337                 case CMD_LOCAL_REQUEST_REKEY_IKE: // Fallthrough
338                 case CMD_LOCAL_REQUEST_INFO: // Fallthrough
339                 case CMD_LOCAL_REQUEST_DPD: // Fallthrough
340                 case CMD_LOCAL_REQUEST_CREATE_CHILD: // Fallthrough
341                 case CMD_LOCAL_REQUEST_DELETE_CHILD: // Fallthrough
342                 case CMD_LOCAL_REQUEST_REKEY_CHILD:
343                     return REQUEST_PRIORITY_NORMAL;
344 
345                 default:
346                     // unknown procedure type - assign it the lowest priority
347                     getIkeLog().wtf(TAG, "Unknown procedureType: " + procedureType);
348                     return REQUEST_PRIORITY_UNKNOWN;
349             }
350         }
351     }
352 }
353