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
17package com.android.internal.telephony;
18
19import android.os.AsyncResult;
20import android.os.Message;
21import android.os.SystemClock;
22import android.os.WorkSource;
23import android.os.WorkSource.WorkChain;
24import android.telephony.Rlog;
25
26import java.util.ArrayList;
27import java.util.Random;
28import java.util.concurrent.atomic.AtomicInteger;
29
30/**
31 * {@hide}
32 */
33
34public 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
56    public int getSerial() {
57        return mSerial;
58    }
59
60    public int getRequest() {
61        return mRequest;
62    }
63
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     */
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
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
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     */
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
175    private RILRequest() {
176    }
177
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
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
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