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