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

Commit c6dd471d authored by Anurag Singh's avatar Anurag Singh Committed by Giulio Cervera
Browse files

frameworks/base: Vote to turn off io_is_busy during recording.

When recording starts, vote to turn off io_is_busy. After
recording stops, take out that vote so that io_is_busy can
go to its original value. Doing this saves power by reducing
the amount of time for which the CPU runs at high frequencies
when encoded data is being written to the SD card - a process
that causes io_wait times to shoot up. With io_is_busy turned off,
these wait times will not be considered as CPU busy time and so
the ondemand governor will not unnecessarily bump up the clock rate.
The votes are handled by the CpuGovernorService. Also move the
dynamic sampling rate and vote processor classes out of the
CpuGovernorService to improve modularity.
parent 377a18a6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -10811,6 +10811,8 @@ package android.media {
    ctor public MediaRecorder();
    method public static final int getAudioSourceMax();
    method public int getMaxAmplitude() throws java.lang.IllegalStateException;
    method public void native_start() throws java.lang.IllegalStateException;
    method public void native_stop() throws java.lang.IllegalStateException;
    method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
    method public void release();
    method public void reset();
+37 −3
Original line number Diff line number Diff line
@@ -22,6 +22,10 @@ import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.Surface;
import android.app.Application;
import android.app.ActivityThread;
import android.content.Context;
import android.content.Intent;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -76,8 +80,10 @@ public class MediaRecorder
        System.loadLibrary("media_jni");
        native_init();
    }
    private final static String TAG = "MediaRecorder";

    private final static String TAG = "MediaRecorder";
    private final static String IOBUSY_VOTE = "com.android.server.CpuGovernorService.action.IOBUSY_VOTE";
    private final static String IOBUSY_UNVOTE = "com.android.server.CpuGovernorService.action.IOBUSY_UNVOTE";
    // The two fields below are accessed by native methods
    @SuppressWarnings("unused")
    private int mNativeContext;
@@ -654,7 +660,7 @@ public class MediaRecorder
     * @throws IllegalStateException if it is called before
     * prepare().
     */
    public native void start() throws IllegalStateException;
    public native void native_start() throws IllegalStateException;

    /**
     * Stops recording. Call this after start(). Once recording is stopped,
@@ -668,7 +674,35 @@ public class MediaRecorder
     *
     * @throws IllegalStateException if it is called before start()
     */
    public native void stop() throws IllegalStateException;
    public native void native_stop() throws IllegalStateException;

    public void start() throws IllegalStateException {
        try {
            Application application = ActivityThread.systemMain().getApplication();
            Intent ioBusyVoteIntent = new Intent(IOBUSY_VOTE);
            // Vote for io_is_busy to be turned off.
            ioBusyVoteIntent.putExtra("com.android.server.CpuGovernorService.voteType", 0);
            application.sendBroadcast(ioBusyVoteIntent);
        } catch (Exception exception) {
            Log.e(TAG, "Unable to vote to turn io_is_busy off.");
        }

        native_start();
    }

    public void stop() throws IllegalStateException {
        try {
            Application application = ActivityThread.systemMain().getApplication();
            Intent ioBusyUnVoteIntent = new Intent(IOBUSY_UNVOTE);
            // Remove vote for io_is_busy to be turned off.
            ioBusyUnVoteIntent.putExtra("com.android.server.CpuGovernorService.voteType", 0);
            application.sendBroadcast(ioBusyUnVoteIntent);
        } catch (Exception exception) {
            Log.e(TAG, "Unable to withdraw io_is_busy off vote.");
        }

        native_stop();
    }

    /**
     * Restarts the MediaRecorder to its idle state. After calling
+2 −2
Original line number Diff line number Diff line
@@ -467,8 +467,8 @@ static JNINativeMethod gMethods[] = {
    {"setMaxFileSize",       "(J)V",                            (void *)android_media_MediaRecorder_setMaxFileSize},
    {"_prepare",             "()V",                             (void *)android_media_MediaRecorder_prepare},
    {"getMaxAmplitude",      "()I",                             (void *)android_media_MediaRecorder_native_getMaxAmplitude},
    {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
    {"stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
    {"native_start",                "()V",                             (void *)android_media_MediaRecorder_start},
    {"native_stop",                 "()V",                             (void *)android_media_MediaRecorder_stop},
    {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
    {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
    {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
+324 −41
Original line number Diff line number Diff line
/*
 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
@@ -49,20 +49,12 @@ import android.text.format.Time;
import android.util.Log;

class CpuGovernorService {
    private final String SAMPLING_RATE_FILE_PATH =
        "/sys/devices/system/cpu/cpufreq/ondemand/sampling_rate";
    private final String SCREEN_OFF_SAMPLING_RATE = "500000";
    private final String TAG = "CpuGovernorService";
    private final int SAMPLING_RATE_INCREASE = 1;
    private final int SAMPLING_RATE_DECREASE = 2;
    private final int MAX_SAMPLING_RATE_LENGTH = 32;
    private Context mContext;
    private String mSavedSamplingRate = "0";
    private Vector<Integer> mSamplingRateChanges = new Vector<Integer>();
    private Object mSynchSamplingRateChanges = new Object();
    private boolean mNotificationPending = false;
    private SamplingRateChangeProcessor samplingRateChangeProcessor =
    private SamplingRateChangeProcessor mSamplingRateChangeProcessor =
        new SamplingRateChangeProcessor();
    private IOBusyVoteProcessor mIOBusyVoteChangeProcessor =
        new IOBusyVoteProcessor();

    public CpuGovernorService(Context context) {
        mContext = context;
@@ -70,7 +62,10 @@ class CpuGovernorService {

        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        new Thread(samplingRateChangeProcessor).start();
        intentFilter.addAction(IOBusyVoteProcessor.ACTION_IOBUSY_VOTE);
        intentFilter.addAction(IOBusyVoteProcessor.ACTION_IOBUSY_UNVOTE);
        new Thread(mSamplingRateChangeProcessor).start();
        new Thread(mIOBusyVoteChangeProcessor).start();
        mContext.registerReceiver(mReceiver, intentFilter);
    }

@@ -79,43 +74,330 @@ class CpuGovernorService {
        public void onReceive(Context context, Intent intent) {
            boolean changeAdded = false;

            Log.i(TAG, "intent action: " + intent.getAction());

            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
                if (SystemProperties.getInt("dev.pm.dyn_samplingrate", 0) != 0) {
                    while (!changeAdded) {
                        try {
                            mSamplingRateChanges.add(SAMPLING_RATE_DECREASE);
                            mSamplingRateChangeProcessor.getSamplingRateChangeRequests().
                                add(SamplingRateChangeProcessor.SAMPLING_RATE_DECREASE);
                            changeAdded = true;
                        } catch (ConcurrentModificationException concurrentModificationException) {
                            // Ignore and try again.
                        }
                    }

                    synchronized(mSynchSamplingRateChanges) {
                        mSynchSamplingRateChanges.notify();
                        mNotificationPending = true;
                    synchronized (mSamplingRateChangeProcessor.getSynchObject()) {
                        mSamplingRateChangeProcessor.getSynchObject().notify();
                        mSamplingRateChangeProcessor.setNotificationPending(true);
                    }
                }
            } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                if (SystemProperties.getInt("dev.pm.dyn_samplingrate", 0) != 0) {
                    while (!changeAdded) {
                        try {
                            mSamplingRateChanges.add(SAMPLING_RATE_INCREASE);
                            mSamplingRateChangeProcessor.getSamplingRateChangeRequests().
                                add(SamplingRateChangeProcessor.SAMPLING_RATE_INCREASE);
                            changeAdded = true;
                        } catch (ConcurrentModificationException concurrentModificationException) {
                            // Ignore and try again.
                        }
                    }

                    synchronized(mSynchSamplingRateChanges) {
                        mSynchSamplingRateChanges.notify();
                        mNotificationPending = true;
                    synchronized (mSamplingRateChangeProcessor.getSynchObject()) {
                        mSamplingRateChangeProcessor.getSynchObject().notify();
                        mSamplingRateChangeProcessor.setNotificationPending(true);
                    }
                }
            } else if (intent.getAction().equals(IOBusyVoteProcessor.ACTION_IOBUSY_VOTE)) {
                int voteType = intent.getExtras().getInt("com.android.server.CpuGovernorService.voteType");
                Log.i(TAG, "IOBUSY vote: " + voteType);

                while (!changeAdded) {
                    try {
                        if (voteType == 1) {
                            mIOBusyVoteChangeProcessor.getIOBusyChangeRequests().
                                add(IOBusyVoteProcessor.IO_IS_BUSY_VOTE_ON);
                        } else {
                            mIOBusyVoteChangeProcessor.getIOBusyChangeRequests().
                                add(IOBusyVoteProcessor.IO_IS_BUSY_VOTE_OFF);
                        }
                        changeAdded = true;
                    } catch (ConcurrentModificationException concurrentModificationException) {
                        // Ignore and try again.
                    }
                }

                synchronized (mIOBusyVoteChangeProcessor.getSynchObject()) {
                    mIOBusyVoteChangeProcessor.getSynchObject().notify();
                    mIOBusyVoteChangeProcessor.setNotificationPending(true);
                }
            } else if (intent.getAction().equals(IOBusyVoteProcessor.ACTION_IOBUSY_UNVOTE)) {
                int voteType = intent.getExtras().getInt("com.android.server.CpuGovernorService.voteType");
                Log.i(TAG, "IOBUSY unvote: " + voteType);

                while (!changeAdded) {
                    try {
                        if (voteType == 1) {
                            mIOBusyVoteChangeProcessor.getIOBusyChangeRequests().
                                add(IOBusyVoteProcessor.IO_IS_BUSY_UNVOTE_ON);
                        } else {
                            mIOBusyVoteChangeProcessor.getIOBusyChangeRequests().
                                add(IOBusyVoteProcessor.IO_IS_BUSY_UNVOTE_OFF);
                        }
                        changeAdded = true;
                    } catch (ConcurrentModificationException concurrentModificationException) {
                        // Ignore and try again.
                    }
                }

                synchronized (mIOBusyVoteChangeProcessor.getSynchObject()) {
                    mIOBusyVoteChangeProcessor.getSynchObject().notify();
                    mIOBusyVoteChangeProcessor.setNotificationPending(true);
                }
            }
        }
    };
}

class IOBusyVoteProcessor implements Runnable {
    private final String TAG = "IOBusyVoteProcessor";
    private static final String IO_IS_BUSY_FILE_PATH =
        "/sys/devices/system/cpu/cpufreq/ondemand/io_is_busy";
    private final int MAX_IO_IS_BUSY_VALUE_LENGTH = 32;
    private boolean mNotificationPending = false;
    private Vector<Integer> mIOBusyChanges = new Vector<Integer>();
    private Object mSynchIOBusyChanges = new Object();
    private int mSavedIOBusyValue = -1;
    private int mCurrentIOBusyValue = -1;
    private int mOnVotes = 0;
    private int mOffVotes = 0;
    private boolean mError = false;

    public static final int IO_IS_BUSY_VOTE_ON = 1;
    public static final int IO_IS_BUSY_VOTE_OFF = 2;
    public static final int IO_IS_BUSY_UNVOTE_ON = 3;
    public static final int IO_IS_BUSY_UNVOTE_OFF = 4;
    public static final String ACTION_IOBUSY_VOTE = "com.android.server.CpuGovernorService.action.IOBUSY_VOTE";
    public static final String ACTION_IOBUSY_UNVOTE = "com.android.server.CpuGovernorService.action.IOBUSY_UNVOTE";

    public void setNotificationPending(boolean notificationPending) {
        mNotificationPending = notificationPending;
    }

    public boolean getNotificationPending() {
        return mNotificationPending;
    }

    public Vector<Integer> getIOBusyChangeRequests() {
        return mIOBusyChanges;
    }

    public Object getSynchObject() {
        return mSynchIOBusyChanges;
    }

    public void initializeIOBusyValue() {
        mSavedIOBusyValue = getCurrentIOBusyValue();
        mCurrentIOBusyValue = mSavedIOBusyValue;
    }

    public void run() {
        while (true && !mError) {
            try {
                synchronized (mSynchIOBusyChanges) {
                    if (!mNotificationPending) {
                        mSynchIOBusyChanges.wait();
                    }

                    mNotificationPending = false;
                }
            } catch (InterruptedException interruptedException) {
            }

            while (!mIOBusyChanges.isEmpty()) {
                try{
                    int ioBusyChangeRequestType = mIOBusyChanges.remove(0);

                    if (mOnVotes == 0 && mOffVotes == 0) {
                        // There are no votes in the system. This is a good time
                        // to set the saved io_is_busy value.
                        initializeIOBusyValue();
                    }

                    if (mError) {
                        break;
                    }

                    if (ioBusyChangeRequestType == IO_IS_BUSY_VOTE_ON) {
                        voteOn();
                    } else if (ioBusyChangeRequestType == IO_IS_BUSY_VOTE_OFF) {
                        voteOff();
                    } else if (ioBusyChangeRequestType == IO_IS_BUSY_UNVOTE_ON) {
                        unvoteOn();
                    } else if (ioBusyChangeRequestType == IO_IS_BUSY_UNVOTE_OFF) {
                        unvoteOff();
                    }
                } catch (ConcurrentModificationException concurrentModificationException) {
                    // Ignore and make the thread try again.
                }
            }
        }
    }

    private void voteOn() {
        mCurrentIOBusyValue = 1;
        setIOBusyValue(mCurrentIOBusyValue);
        mOnVotes++;
    }

    private void unvoteOn() {
        if (mOnVotes == 0) {
            Log.e(TAG, "On votes can't be negative.");

            return;
        }

        mOnVotes--;

        if (mOnVotes == 0) {
            // There are no more on votes. If there are no more
            // off votes either, we can go to the orinigal io_is_busy
            // state. Otherwise, we respect the off votes and turn
            // io_is_busy off.
            if (mOffVotes == 0) {
                mCurrentIOBusyValue = mSavedIOBusyValue;
                setIOBusyValue(mCurrentIOBusyValue);
            } else if (mOffVotes > 0) {
                mCurrentIOBusyValue = 0;
                setIOBusyValue(mCurrentIOBusyValue);
            } else {
                mError = true;

                Log.e(TAG, "Off votes can't be negative.");
            }
        }
    }

    private void voteOff() {
        if (mOnVotes == 0) {
            mCurrentIOBusyValue = 0;
            setIOBusyValue(mCurrentIOBusyValue);
        }

        mOffVotes++;
    }

    private void unvoteOff() {
        if (mOffVotes == 0) {
            Log.e(TAG, "Off votes can't be negative.");

            return;
        }

        mOffVotes--;

        if (mOffVotes == 0 && mOnVotes == 0) {
            mCurrentIOBusyValue = mSavedIOBusyValue;
            setIOBusyValue(mCurrentIOBusyValue);
        }
    }

    /*
     * Set the passed in ioBusyValue as the current
     * value of io_is_busy.
     */
    private void setIOBusyValue(int ioBusyValue) {
        File fileIOBusy = new File(IO_IS_BUSY_FILE_PATH);

        if (fileIOBusy.canWrite()) {
            try {
                PrintWriter ioBusyValueWriter = new PrintWriter(fileIOBusy);
                ioBusyValueWriter.print(ioBusyValue + "");
                ioBusyValueWriter.close();
                Log.i(TAG, "Set io_is_busy to " + ioBusyValue);
            } catch (Exception exception) {
                mError = true;

                Log.e(TAG, "Unable to write to io_is_busy.");
            }
        } else {
            mError = true;

            Log.e(TAG, "io_is_busy cannot be written to.");
        }
    }

    /*
     * Get the current io_is_busy value by reading the file.
     */
    private int getCurrentIOBusyValue() {
        File fileIOBusy = new File(IO_IS_BUSY_FILE_PATH);
        int ioBusyValue = -1;

        if (fileIOBusy.canRead()) {
            try {
                BufferedReader ioBusyValueReader = new BufferedReader(
                        new FileReader(fileIOBusy));
                char[] ioBusyContents = new char[MAX_IO_IS_BUSY_VALUE_LENGTH];

                ioBusyValueReader.read(ioBusyContents, 0,
                        MAX_IO_IS_BUSY_VALUE_LENGTH - 1);
                ioBusyValueReader.close();

                try {
                    ioBusyValue = Integer.parseInt((new String(ioBusyContents)).trim());
                } catch (Exception exception) {
                    mError = true;

                    Log.e(TAG, "Unable to read io_is_busy. Contents: " + new String(ioBusyContents));
                }
            } catch (Exception exception) {
                mError = true;

                Log.e(TAG, "io_is_busy cannot be read.");
            }
        } else {
            mError = true;

            Log.e(TAG, "io_is_busy cannot be read.");
        }

        return ioBusyValue;
    }
}

class SamplingRateChangeProcessor implements Runnable {
    private final String TAG = "SamplingRateChangeProcessor";
    private static final String SAMPLING_RATE_FILE_PATH =
        "/sys/devices/system/cpu/cpufreq/ondemand/sampling_rate";
    private static final String SCREEN_OFF_SAMPLING_RATE = "500000";
    private boolean mNotificationPending = false;
    private Vector<Integer> mSamplingRateChanges = new Vector<Integer>();
    private Object mSynchSamplingRateChanges = new Object();
    private String mSavedSamplingRate = "0";
    private int MAX_SAMPLING_RATE_LENGTH = 32;

    public static final int SAMPLING_RATE_INCREASE = 1;
    public static final int SAMPLING_RATE_DECREASE = 2;

    public void setNotificationPending(boolean notificationPending) {
        mNotificationPending = notificationPending;
    }

    public boolean getNotificationPending() {
        return mNotificationPending;
    }

    public Vector<Integer> getSamplingRateChangeRequests() {
        return mSamplingRateChanges;
    }

    public Object getSynchObject() {
        return mSynchSamplingRateChanges;
    }

    private class SamplingRateChangeProcessor implements Runnable {
    public void run() {
        while (true) {
            try {
@@ -144,7 +426,6 @@ class CpuGovernorService {
            }
        }
    }
    }

    private void increaseSamplingRate() {
        File fileSamplingRate = new File(SAMPLING_RATE_FILE_PATH);
@@ -188,3 +469,5 @@ class CpuGovernorService {
        }
    }
}