Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75af4b92 authored by Naveen Kalla's avatar Naveen Kalla Committed by Sooraj Sasindran
Browse files

Attribute wakelocks held by RIL to clients

Telephony services are requested by applications and other
services in the Android framework. Attribute the wakelock
held time to the applications and services that requested
the particular operation.

If multiple clients are requesting for a service at the same
time, divide the time equally between all the requests

Test: Through logs confirmed that the wakelock decrement is happening
    correctly
BUG=31041780
Merged-In: Id5521809d5a4324c1bd9dc229a743033886b35a3
Change-Id: Id5521809d5a4324c1bd9dc229a743033886b35a3
parent d4da8408
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.telephony.ClientRequestStats;
import android.telephony.Rlog;

import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;

public class ClientWakelockAccountant {
    public static final String LOG_TAG = "ClientWakelockAccountant: ";

    @VisibleForTesting
    public ClientRequestStats mRequestStats = new ClientRequestStats();
    @VisibleForTesting
    public ArrayList<RilWakelockInfo> mPendingRilWakelocks = new ArrayList<>();

    @VisibleForTesting
    public ClientWakelockAccountant(String callingPackage) {
        mRequestStats.setCallingPackage(callingPackage);
    }

    @VisibleForTesting
    public void startAttributingWakelock(int request,
            int token, int concurrentRequests, long time) {

        RilWakelockInfo wlInfo = new RilWakelockInfo(request, token, concurrentRequests, time);
        synchronized (mPendingRilWakelocks) {
            mPendingRilWakelocks.add(wlInfo);
        }
    }

    @VisibleForTesting
    public void stopAttributingWakelock(int request, int token, long time) {
        RilWakelockInfo wlInfo = removePendingWakelock(request, token);
        if (wlInfo != null) {
            completeRequest(wlInfo, time);
        }
    }

    @VisibleForTesting
    public void stopAllPendingRequests(long time) {
        synchronized (mPendingRilWakelocks) {
            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
                completeRequest(wlInfo, time);
            }
            mPendingRilWakelocks.clear();
        }
    }

    @VisibleForTesting
    public void changeConcurrentRequests(int concurrentRequests, long time) {
        synchronized (mPendingRilWakelocks) {
            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
                wlInfo.updateConcurrentRequests(concurrentRequests, time);
            }
        }
    }

    private void completeRequest(RilWakelockInfo wlInfo, long time) {
        wlInfo.setResponseTime(time);
        synchronized (mRequestStats) {
            mRequestStats.addCompletedWakelockTime(wlInfo.getWakelockTimeAttributedToClient());
            mRequestStats.incrementCompletedRequestsCount();
            mRequestStats.updateRequestHistograms(wlInfo.getRilRequestSent(),
                    (int) wlInfo.getWakelockTimeAttributedToClient());
        }
    }

    @VisibleForTesting
    public int getPendingRequestCount() {
        return mPendingRilWakelocks.size();
    }

    @VisibleForTesting
    public synchronized long updatePendingRequestWakelockTime(long uptime) {
        long totalPendingWakelockTime = 0;
        synchronized (mPendingRilWakelocks) {
            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
                wlInfo.updateTime(uptime);
                totalPendingWakelockTime += wlInfo.getWakelockTimeAttributedToClient();
            }
        }
        synchronized (mRequestStats) {
            mRequestStats.setPendingRequestsCount(getPendingRequestCount());
            mRequestStats.setPendingRequestsWakelockTime(totalPendingWakelockTime);
        }
        return totalPendingWakelockTime;
    }

    private RilWakelockInfo removePendingWakelock(int request, int token) {
        RilWakelockInfo result = null;
        synchronized (mPendingRilWakelocks) {
            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
                if ((wlInfo.getTokenNumber() == token) &&
                    (wlInfo.getRilRequestSent() == request)) {
                    result = wlInfo;
                }
            }
            if( result != null ) {
                mPendingRilWakelocks.remove(result);
            }
        }
        if(result == null) {
            Rlog.w(LOG_TAG, "Looking for Request<" + request + "," + token + "> in "
                + mPendingRilWakelocks);
        }
        return result;
    }

    @Override
    public String toString() {
        return "ClientWakelockAccountant{" +
                "mRequestStats=" + mRequestStats +
                ", mPendingRilWakelocks=" + mPendingRilWakelocks +
                '}';
    }
}
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.os.SystemClock;
import android.telephony.ClientRequestStats;
import android.telephony.Rlog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ClientWakelockTracker {
    public static final String LOG_TAG = "ClientWakelockTracker";
    @VisibleForTesting
    public HashMap<String, ClientWakelockAccountant> mClients =
        new HashMap<String, ClientWakelockAccountant>();
    @VisibleForTesting
    public ArrayList<ClientWakelockAccountant> mActiveClients = new ArrayList<>();

    @VisibleForTesting
    public void startTracking(String clientId, int requestId, int token, int numRequestsInQueue) {
        ClientWakelockAccountant client = getClientWakelockAccountant(clientId);
        long uptime = SystemClock.uptimeMillis();
        client.startAttributingWakelock(requestId, token, numRequestsInQueue, uptime);
        updateConcurrentRequests(numRequestsInQueue, uptime);
        synchronized (mActiveClients) {
            if (!mActiveClients.contains(client)) {
                mActiveClients.add(client);
            }
        }
    }

    @VisibleForTesting
    public void stopTracking(String clientId, int requestId, int token, int numRequestsInQueue) {
        ClientWakelockAccountant client = getClientWakelockAccountant(clientId);
        long uptime = SystemClock.uptimeMillis();
        client.stopAttributingWakelock(requestId, token, uptime);
        if(client.getPendingRequestCount() == 0) {
            synchronized (mActiveClients) {
                mActiveClients.remove(client);
            }
        }
        updateConcurrentRequests(numRequestsInQueue, uptime);
    }

    @VisibleForTesting
    public void stopTrackingAll() {
        long uptime = SystemClock.uptimeMillis();
        synchronized (mActiveClients) {
            for (ClientWakelockAccountant client : mActiveClients) {
                client.stopAllPendingRequests(uptime);
            }
            mActiveClients.clear();
        }
    }

    List<ClientRequestStats> getClientRequestStats() {
        List<ClientRequestStats> list;
        long uptime = SystemClock.uptimeMillis();
        synchronized (mClients) {
            list = new ArrayList<>(mClients.size());
            for (String key :  mClients.keySet()) {
                ClientWakelockAccountant client = mClients.get(key);
                client.updatePendingRequestWakelockTime(uptime);
                list.add(new ClientRequestStats(client.mRequestStats));
            }
        }
        return list;
    }

    private ClientWakelockAccountant getClientWakelockAccountant(String clientId) {
        ClientWakelockAccountant client;
        synchronized (mClients) {
            if (mClients.containsKey(clientId)) {
                client = mClients.get(clientId);
            } else {
                client = new ClientWakelockAccountant(clientId);
                mClients.put(clientId, client);
            }
        }
        return client;
    }

    private void updateConcurrentRequests(int numRequestsInQueue, long time) {
        if(numRequestsInQueue != 0) {
            synchronized (mActiveClients) {
                for (ClientWakelockAccountant cI : mActiveClients) {
                    cI.changeConcurrentRequests(numRequestsInQueue, time);
                }
            }
        }
    }

    void dumpClientRequestTracker() {
        Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------");
        synchronized (mClients) {
            for (String key : mClients.keySet()) {
                Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key);
                Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString());
            }
        }
    }
}
 No newline at end of file
+29 −1
Original line number Diff line number Diff line
@@ -112,7 +112,6 @@ class RILRequest {
    private static RILRequest sPool = null;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 4;
    private Context mContext;

    //***** Instance Variables
    int mSerial;
@@ -121,6 +120,7 @@ class RILRequest {
    Parcel mParcel;
    RILRequest mNext;
    int mWakeLockType;
    String mClientId;
    // time in ms when RIL request was made
    long mStartTimeMs;

@@ -154,6 +154,7 @@ class RILRequest {
        rr.mParcel = Parcel.obtain();

        rr.mWakeLockType = RIL.INVALID_WAKELOCK;
        rr.mClientId = ActivityThread.currentOpPackageName();
        rr.mStartTimeMs = SystemClock.elapsedRealtime();
        if (result != null && result.getTarget() == null) {
            throw new NullPointerException("Message target must not be null");
@@ -166,6 +167,26 @@ class RILRequest {
        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 clientId Id to track the client which initiated this request
     * @return a RILRequest instance from the pool.
     */
    static RILRequest obtain(int request, Message result, String clientId) {
        RILRequest rr = null;

        rr = obtain(request, result);
        if(clientId != null) {
            rr.mClientId = clientId;
        }

        return rr;
    }

    /**
     * Returns a RILRequest instance to the pool.
     *
@@ -274,6 +295,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
    public static final int INVALID_WAKELOCK = -1;
    public static final int FOR_WAKELOCK = 0;
    public static final int FOR_ACK_WAKELOCK = 1;
    private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();

    //***** Instance Variables

@@ -2785,6 +2807,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                        mWakeLock.acquire();
                        mWakeLockCount++;
                        mWlSequenceNum++;
                        mClientWakelockTracker.startTracking(rr.mClientId,
                                rr.mRequest, rr.mSerial, mWakeLockCount);

                        Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
                        msg.arg1 = mWlSequenceNum;
@@ -2821,6 +2845,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                            mWakeLockCount = 0;
                            mWakeLock.release();
                        }
                        mClientWakelockTracker.stopTracking(rr.mClientId,
                                rr.mRequest, rr.mSerial, mWakeLockCount);
                    }
                    break;
                case FOR_ACK_WAKELOCK:
@@ -2844,6 +2870,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
                        + "at time of clearing");
                mWakeLockCount = 0;
                mWakeLock.release();
                mClientWakelockTracker.stopTrackingAll();
                return true;
            }
        } else {
@@ -4545,6 +4572,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
        }
        pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
        pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
        mClientWakelockTracker.dumpClientRequestTracker();
    }

    /**
+109 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2006 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.TargetApi;
import android.os.Build;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;

@TargetApi(8)
public class RilWakelockInfo {
    private final String LOG_TAG = RilWakelockInfo.class.getSimpleName();
    private int mRilRequestSent;
    private int mTokenNumber;
    private long mRequestTime;
    private long mResponseTime;

    /* If there are n requests waiting for a response for time t, the time attributed to
    each request will be t/n. If the number of outstanding requests changes at time t1,
    then we will compute the wakelock time till t1 and store it in mWakelockTimeAttributedSoFar
    and update mConcurrentRequests. mLastAggregatedTime will be set to t1 and used to
    compute the time taken for this request using the new mConcurrentRequests
     */
    private long mWakelockTimeAttributedSoFar;
    private long mLastAggregatedTime;
    private int mConcurrentRequests;

    @VisibleForTesting
    public int getConcurrentRequests() {
        return mConcurrentRequests;
    }

    RilWakelockInfo(int rilRequest, int tokenNumber, int concurrentRequests, long requestTime) {
        concurrentRequests = validateConcurrentRequests(concurrentRequests);
        this.mRilRequestSent = rilRequest;
        this.mTokenNumber = tokenNumber;
        this.mConcurrentRequests = concurrentRequests;
        this.mRequestTime = requestTime;
        this.mWakelockTimeAttributedSoFar = 0;
        this.mLastAggregatedTime = requestTime;
    }

    private int validateConcurrentRequests(int concurrentRequests) {
        if(concurrentRequests <= 0) {
            if(Build.IS_DEBUGGABLE) {
                IllegalArgumentException e = new IllegalArgumentException(
                    "concurrentRequests should always be greater than 0.");
                Log.wtf(LOG_TAG, e);
                throw e;
            } else {
                concurrentRequests = 1;
            }
        }
        return concurrentRequests;
    }

    int getTokenNumber() {
        return mTokenNumber;
    }

    int getRilRequestSent() {
        return mRilRequestSent;
    }

    void setResponseTime(long responseTime) {
        updateTime(responseTime);
        this.mResponseTime = responseTime;
    }

    void updateConcurrentRequests(int concurrentRequests, long time) {
        concurrentRequests = validateConcurrentRequests(concurrentRequests);
        updateTime(time);
        mConcurrentRequests = concurrentRequests;
    }

    synchronized void updateTime(long time) {
        mWakelockTimeAttributedSoFar += (time - mLastAggregatedTime) / mConcurrentRequests;
        mLastAggregatedTime = time;
    }

    long getWakelockTimeAttributedToClient() {
        return mWakelockTimeAttributedSoFar;
    }

    @Override
    public String toString() {
        return "WakelockInfo{" +
                "rilRequestSent=" + mRilRequestSent +
                ", tokenNumber=" + mTokenNumber +
                ", requestTime=" + mRequestTime +
                ", responseTime=" + mResponseTime +
                ", mWakelockTimeAttributed=" + mWakelockTimeAttributedSoFar +
                '}';
    }
}
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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 static com.android.internal.telephony.TelephonyTestUtils.waitForMs;

import android.test.TestRunner;
import android.os.Build;
import android.util.Log;
import android.telephony.Rlog;
import android.telephony.TelephonyHistogram;

import junit.framework.Assert;
import junit.framework.TestCase;

public class ClientWakelockAccountantTest extends TestCase {
    private final static String LOG_TAG = "ClientWakelockAccountantTest";
    ClientWakelockAccountant mClient;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mClient = new ClientWakelockAccountant("Package Name");
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    public void testStartAttributingWakelock() throws Exception {

        Assert.assertEquals(0, mClient.getPendingRequestCount());
        mClient.startAttributingWakelock(15, 25, 1, 100);
        Assert.assertEquals(1, mClient.getPendingRequestCount());
        mClient.startAttributingWakelock(22, 26, 2, 150);
        Assert.assertEquals(2, mClient.getPendingRequestCount());
    }

    /* This test only tests ClientWakelockAccountant to make sure that it applies the
       multiplier as expected. Here we start 1 Client and add 2 requests.
       First request multiplier stays at 1 and goes on for 200ms and second request
       multiplier stays at 0.5 and request goes on for 500ms. So totally we
       expect the wakelock time for the Client to be 200*1 + 500*0.5 = 450ms
     */
    public void testStopAttributingWakelock() throws Exception {
        mClient.startAttributingWakelock(15, 25, 1, 100);
        mClient.startAttributingWakelock(25, 26, 2, 200);
        mClient.changeConcurrentRequests(2, 200);
        Assert.assertEquals(2, mClient.getPendingRequestCount());
        Assert.assertEquals(0, mClient.mRequestStats.getCompletedRequestsCount());
        mClient.stopAttributingWakelock(15, 25, 300);
        mClient.changeConcurrentRequests(1, 300);
        Assert.assertEquals(1, mClient.mRequestStats.getRequestHistograms().size());
        mClient.stopAttributingWakelock(25, 26, 700);
        Assert.assertEquals(0, mClient.getPendingRequestCount());
        Assert.assertEquals(2, mClient.mRequestStats.getCompletedRequestsCount());
        Assert.assertEquals(600, mClient.mRequestStats.getCompletedRequestsWakelockTime());
        Assert.assertEquals(0, mClient.updatePendingRequestWakelockTime(0));
        Assert.assertEquals(2, mClient.mRequestStats.getRequestHistograms().size());
    }

    public void testStopAllPendingRequests() throws Exception {

        mClient.startAttributingWakelock(15, 25, 1, 100);
        mClient.startAttributingWakelock(22, 26, 2, 150);
        Assert.assertEquals(2, mClient.getPendingRequestCount());
        mClient.stopAllPendingRequests(300);
        Assert.assertEquals(0, mClient.getPendingRequestCount());
        Assert.assertEquals(2, mClient.mRequestStats.getCompletedRequestsCount());
        Assert.assertEquals(275, mClient.mRequestStats.getCompletedRequestsWakelockTime());
        Assert.assertEquals(0, mClient.updatePendingRequestWakelockTime(0));
        Assert.assertEquals(2, mClient.mRequestStats.getRequestHistograms().size());
    }

    public void testStartAttributingWithZeroConcurrentRequests() throws Exception {
        if(Build.IS_DEBUGGABLE) {
            try {
                mClient.startAttributingWakelock(15, 25, 0, 100);
                fail("Expecting an illegal argument Exception to be thrown");
            } catch (IllegalArgumentException e) { }
        } else {
            mClient.startAttributingWakelock(15, 25, 0, 100);
            Assert.assertEquals(1, mClient.getPendingRequestCount());
        }
    }
}
Loading