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