content: /*
   * 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.annotation.UnsupportedAppUsage;
  import android.os.AsyncResult;
  import android.os.Message;
  import android.os.SystemClock;
  import android.os.WorkSource;
  import android.os.WorkSource.WorkChain;
  import android.telephony.Rlog;
  
  import java.util.ArrayList;
  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;
  
      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;
      }
  
      /**
       * 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.get(0) + ":" + mWorkSource.getName(0);
          }
  
          final ArrayList<WorkChain> workChains = mWorkSource.getWorkChains();
          if (workChains != null && !workChains.isEmpty()) {
              final WorkChain workChain = workChains.get(0);
              return workChain.getAttributionUid() + ":" + workChain.getTags()[0];
          }
  
          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());
                      }
                  }
              }
          }
      }
  
      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(int error, Object ret) {
          CommandException ex;
  
          ex = CommandException.fromRilErrno(error);
  
          if (RIL.RILJ_LOGD) {
              Rlog.d(LOG_TAG, serialString() + "< "
                      + RIL.requestToString(mRequest)
                      + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
          }
  
          if (mResult != null) {
              AsyncResult.forMessage(mResult, ret, ex);
              mResult.sendToTarget();
          }
      }
  }
  