1 /*
2  * Copyright (C) 2017 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 
17 package com.android.internal.telephony;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.AsyncResult;
21 import android.os.Message;
22 import android.os.SystemClock;
23 import android.os.WorkSource;
24 import android.os.WorkSource.WorkChain;
25 
26 import com.android.telephony.Rlog;
27 
28 import java.util.ArrayList;
29 import java.util.Random;
30 import java.util.concurrent.atomic.AtomicInteger;
31 
32 /**
33  * {@hide}
34  */
35 
36 public class RILRequest {
37     static final String LOG_TAG = "RilRequest";
38 
39     //***** Class Variables
40     static Random sRandom = new Random();
41     static AtomicInteger sNextSerial = new AtomicInteger(0);
42     private static Object sPoolSync = new Object();
43     private static RILRequest sPool = null;
44     private static int sPoolSize = 0;
45     private static final int MAX_POOL_SIZE = 4;
46 
47     //***** Instance Variables
48     @UnsupportedAppUsage
49     int mSerial;
50     @UnsupportedAppUsage
51     int mRequest;
52     @UnsupportedAppUsage
53     Message mResult;
54     RILRequest mNext;
55     int mWakeLockType;
56     WorkSource mWorkSource;
57     String mClientId;
58     // time in ms when RIL request was made
59     long mStartTimeMs;
60     /** Argument list for radio HAL fallback method call */
61     Object[] mArguments;
62 
getSerial()63     public int getSerial() {
64         return mSerial;
65     }
66 
getRequest()67     public int getRequest() {
68         return mRequest;
69     }
70 
getResult()71     public Message getResult() {
72         return mResult;
73     }
74 
75     /**
76      * Retrieves a new RILRequest instance from the pool.
77      *
78      * @param request RIL_REQUEST_*
79      * @param result sent when operation completes
80      * @return a RILRequest instance from the pool.
81      */
82     @UnsupportedAppUsage
obtain(int request, Message result)83     private static RILRequest obtain(int request, Message result) {
84         RILRequest rr = null;
85 
86         synchronized (sPoolSync) {
87             if (sPool != null) {
88                 rr = sPool;
89                 sPool = rr.mNext;
90                 rr.mNext = null;
91                 sPoolSize--;
92             }
93         }
94 
95         if (rr == null) {
96             rr = new RILRequest();
97         }
98 
99         // Increment serial number. Wrap to 0 when reaching Integer.MAX_VALUE.
100         rr.mSerial = sNextSerial.getAndUpdate(n -> ((n + 1) % Integer.MAX_VALUE));
101 
102         rr.mRequest = request;
103         rr.mResult = result;
104 
105         rr.mWakeLockType = RIL.INVALID_WAKELOCK;
106         rr.mWorkSource = null;
107         rr.mStartTimeMs = SystemClock.elapsedRealtime();
108         if (result != null && result.getTarget() == null) {
109             throw new NullPointerException("Message target must not be null");
110         }
111 
112         return rr;
113     }
114 
115 
116     /**
117      * Retrieves a new RILRequest instance from the pool and sets the clientId
118      *
119      * @param request RIL_REQUEST_*
120      * @param result sent when operation completes
121      * @param workSource WorkSource to track the client
122      * @return a RILRequest instance from the pool.
123      */
124     // @VisibleForTesting
obtain(int request, Message result, WorkSource workSource)125     public static RILRequest obtain(int request, Message result, WorkSource workSource) {
126         RILRequest rr = obtain(request, result);
127 
128         if (workSource != null) {
129             rr.mWorkSource = workSource;
130             rr.mClientId = rr.getWorkSourceClientId();
131         } else {
132             Rlog.e(LOG_TAG, "null workSource " + request);
133         }
134 
135         return rr;
136     }
137 
138     /**
139      * Retrieves a new RILRequest instance from the pool and sets the clientId
140      *
141      * @param request RIL_REQUEST_*
142      * @param result sent when operation completes
143      * @param workSource WorkSource to track the client
144      * @param args The list of parameters used to call the fallback HAL method
145      * @return a RILRequest instance from the pool.
146      */
147     // @VisibleForTesting
obtain(int request, Message result, WorkSource workSource, Object... args)148     public static RILRequest obtain(int request, Message result, WorkSource workSource,
149             Object... args) {
150         RILRequest rr = obtain(request, result, workSource);
151 
152         rr.mArguments = args;
153 
154         return rr;
155     }
156 
157     /**
158      * Generate a String client ID from the WorkSource.
159      */
160     // @VisibleForTesting
getWorkSourceClientId()161     public String getWorkSourceClientId() {
162         if (mWorkSource == null || mWorkSource.isEmpty()) {
163             return null;
164         }
165 
166         if (mWorkSource.size() > 0) {
167             return mWorkSource.get(0) + ":" + mWorkSource.getName(0);
168         }
169 
170         final ArrayList<WorkChain> workChains = mWorkSource.getWorkChains();
171         if (workChains != null && !workChains.isEmpty()) {
172             final WorkChain workChain = workChains.get(0);
173             return workChain.toString();
174         }
175 
176         return null;
177     }
178 
179     /**
180      * Returns a RILRequest instance to the pool.
181      *
182      * Note: This should only be called once per use.
183      */
184     @UnsupportedAppUsage
release()185     void release() {
186         synchronized (sPoolSync) {
187             if (sPoolSize < MAX_POOL_SIZE) {
188                 mNext = sPool;
189                 sPool = this;
190                 sPoolSize++;
191                 mResult = null;
192                 if (mWakeLockType != RIL.INVALID_WAKELOCK) {
193                     //This is OK for some wakelock types and not others
194                     if (mWakeLockType == RIL.FOR_WAKELOCK) {
195                         Rlog.e(LOG_TAG, "RILRequest releasing with held wake lock: "
196                                 + serialString());
197                     }
198                 }
199                 mArguments = null;
200             }
201         }
202     }
203 
RILRequest()204     private RILRequest() {
205     }
206 
resetSerial()207     static void resetSerial() {
208         // Use a non-negative random number so that on recovery we probably don't mix old requests
209         // with new.
210         sNextSerial.set(sRandom.nextInt(Integer.MAX_VALUE));
211     }
212 
213     @UnsupportedAppUsage
serialString()214     String serialString() {
215         //Cheesy way to do %04d
216         StringBuilder sb = new StringBuilder(8);
217         String sn;
218 
219         // Truncate mSerial to a number with maximum 4 digits.
220         int adjustedSerial = mSerial % 10000;
221         sn = Integer.toString(adjustedSerial);
222 
223         //sb.append("J[");
224         sb.append('[');
225         for (int i = 0, s = sn.length(); i < 4 - s; i++) {
226             sb.append('0');
227         }
228 
229         sb.append(sn);
230         sb.append(']');
231         return sb.toString();
232     }
233 
234     @UnsupportedAppUsage
onError(int error, Object ret)235     void onError(int error, Object ret) {
236         CommandException ex;
237 
238         ex = CommandException.fromRilErrno(error);
239 
240         if (RIL.RILJ_LOGD) {
241             Rlog.d(LOG_TAG, serialString() + "< "
242                     + RIL.requestToString(mRequest)
243                     + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
244         }
245 
246         if (mResult != null) {
247             AsyncResult.forMessage(mResult, ret, ex);
248             mResult.sendToTarget();
249         }
250     }
251 }
252