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