/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import android.compat.annotation.UnsupportedAppUsage; import android.os.AsyncResult; import android.os.Message; import android.os.SystemClock; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import com.android.telephony.Rlog; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; /** * {@hide} */ public class RILRequest { static final String LOG_TAG = "RilRequest"; //***** Class Variables static Random sRandom = new Random(); static AtomicInteger sNextSerial = new AtomicInteger(0); private static Object sPoolSync = new Object(); private static RILRequest sPool = null; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 4; //***** Instance Variables @UnsupportedAppUsage int mSerial; @UnsupportedAppUsage int mRequest; @UnsupportedAppUsage Message mResult; RILRequest mNext; int mWakeLockType; WorkSource mWorkSource; String mClientId; // time in ms when RIL request was made long mStartTimeMs; /** Argument list for radio HAL fallback method call */ Object[] mArguments; public int getSerial() { return mSerial; } public int getRequest() { return mRequest; } public Message getResult() { return mResult; } /** * Retrieves a new RILRequest instance from the pool. * * @param request RIL_REQUEST_* * @param result sent when operation completes * @return a RILRequest instance from the pool. */ @UnsupportedAppUsage private static RILRequest obtain(int request, Message result) { RILRequest rr = null; synchronized (sPoolSync) { if (sPool != null) { rr = sPool; sPool = rr.mNext; rr.mNext = null; sPoolSize--; } } if (rr == null) { rr = new RILRequest(); } // Increment serial number. Wrap to 0 when reaching Integer.MAX_VALUE. rr.mSerial = sNextSerial.getAndUpdate(n -> ((n + 1) % Integer.MAX_VALUE)); rr.mRequest = request; rr.mResult = result; rr.mWakeLockType = RIL.INVALID_WAKELOCK; rr.mWorkSource = null; rr.mStartTimeMs = SystemClock.elapsedRealtime(); if (result != null && result.getTarget() == null) { throw new NullPointerException("Message target must not be null"); } return rr; } /** * Retrieves a new RILRequest instance from the pool and sets the clientId * * @param request RIL_REQUEST_* * @param result sent when operation completes * @param workSource WorkSource to track the client * @return a RILRequest instance from the pool. */ // @VisibleForTesting public static RILRequest obtain(int request, Message result, WorkSource workSource) { RILRequest rr = obtain(request, result); if (workSource != null) { rr.mWorkSource = workSource; rr.mClientId = rr.getWorkSourceClientId(); } else { Rlog.e(LOG_TAG, "null workSource " + request); } return rr; } /** * Retrieves a new RILRequest instance from the pool and sets the clientId * * @param request RIL_REQUEST_* * @param result sent when operation completes * @param workSource WorkSource to track the client * @param args The list of parameters used to call the fallback HAL method * @return a RILRequest instance from the pool. */ // @VisibleForTesting public static RILRequest obtain(int request, Message result, WorkSource workSource, Object... args) { RILRequest rr = obtain(request, result, workSource); rr.mArguments = args; return rr; } /** * Generate a String client ID from the WorkSource. */ // @VisibleForTesting public String getWorkSourceClientId() { if (mWorkSource == null || mWorkSource.isEmpty()) { return null; } if (mWorkSource.size() > 0) { return mWorkSource.getUid(0) + ":" + mWorkSource.getPackageName(0); } final List workChains = mWorkSource.getWorkChains(); if (workChains != null && !workChains.isEmpty()) { final WorkChain workChain = workChains.get(0); return workChain.toString(); } return null; } /** * Returns a RILRequest instance to the pool. * * Note: This should only be called once per use. */ @UnsupportedAppUsage void release() { synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { mNext = sPool; sPool = this; sPoolSize++; mResult = null; if (mWakeLockType != RIL.INVALID_WAKELOCK) { //This is OK for some wakelock types and not others if (mWakeLockType == RIL.FOR_WAKELOCK) { Rlog.e(LOG_TAG, "RILRequest releasing with held wake lock: " + serialString()); } } mArguments = null; } } } private RILRequest() { } static void resetSerial() { // Use a non-negative random number so that on recovery we probably don't mix old requests // with new. sNextSerial.set(sRandom.nextInt(Integer.MAX_VALUE)); } @UnsupportedAppUsage String serialString() { //Cheesy way to do %04d StringBuilder sb = new StringBuilder(8); String sn; // Truncate mSerial to a number with maximum 4 digits. int adjustedSerial = mSerial % 10000; sn = Integer.toString(adjustedSerial); //sb.append("J["); sb.append('['); for (int i = 0, s = sn.length(); i < 4 - s; i++) { sb.append('0'); } sb.append(sn); sb.append(']'); return sb.toString(); } @UnsupportedAppUsage void onError(final int error, final Object ret) { final CommandException ex = CommandException.fromRilErrno(error); final Message result = mResult; if (RIL.RILJ_LOGD) { Rlog.d(LOG_TAG, serialString() + "< " + RILUtils.requestToString(mRequest) + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret) + " result=" + result); } if (result != null && result.getTarget() != null) { AsyncResult.forMessage(result, ret, ex); result.sendToTarget(); } } @Override public String toString() { return serialString() + ": " + RILUtils.requestToString(mRequest); } }