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

Commit 140049bc authored by Connor O'Brien's avatar Connor O'Brien Committed by Gerrit Code Review
Browse files

Merge changes Ie5fa2605,Ia092e896

* changes:
  Use bpf data when available for single-UID cpu stats
  Use bpf data when available for per-UID cpu stats
parents 49fa6ba3 cd834de6
Loading
Loading
Loading
Loading
+202 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.os;

import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Reads cpu time bpf maps.
 *
 * It is implemented as singletons for each separate set of per-UID times. Get___Instance() method
 * returns the corresponding reader instance. In order to prevent frequent GC, it reuses the same
 * SparseArray to store data read from BPF maps.
 *
 * A KernelCpuUidBpfMapReader instance keeps an error counter. When the number of read errors within
 * that instance accumulates to 5, this instance will reject all further read requests.
 *
 * Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
 * 25ms. KernelCpuUidBpfMapReader always tries to use cache if it is fresh and valid, but it can
 * be disabled through a parameter.
 *
 * A KernelCpuUidBpfMapReader instance is thread-safe. It acquires a write lock when reading the bpf
 * map, releases it right after, then acquires a read lock before returning a BpfMapIterator. Caller
 * is responsible for closing BpfMapIterator (also auto-closable) after reading, otherwise deadlock
 * will occur.
 */
public abstract class KernelCpuUidBpfMapReader {
    private static final int ERROR_THRESHOLD = 5;
    private static final long FRESHNESS_MS = 500L;

    private static final KernelCpuUidBpfMapReader FREQ_TIME_READER =
        new KernelCpuUidFreqTimeBpfMapReader();

    private static final KernelCpuUidBpfMapReader ACTIVE_TIME_READER =
        new KernelCpuUidActiveTimeBpfMapReader();

    private static final KernelCpuUidBpfMapReader CLUSTER_TIME_READER =
        new KernelCpuUidClusterTimeBpfMapReader();

    static KernelCpuUidBpfMapReader getFreqTimeReaderInstance() {
        return FREQ_TIME_READER;
    }

    static KernelCpuUidBpfMapReader getActiveTimeReaderInstance() {
        return ACTIVE_TIME_READER;
    }

    static KernelCpuUidBpfMapReader getClusterTimeReaderInstance() {
        return CLUSTER_TIME_READER;
    }

    final String mTag = this.getClass().getSimpleName();
    private int mErrors = 0;
    private boolean mTracking = false;
    protected SparseArray<long[]> mData = new SparseArray<>();
    private long mLastReadTime = 0;
    protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
    protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
    protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();

    public native boolean startTrackingBpfTimes();

    protected abstract boolean readBpfData();

    /**
     * Returns an array of metadata used to inform the caller of 1) the size of array required by
     * getNextUid and 2) how to interpret the raw data copied to that array.
     */
    public abstract long[] getDataDimensions();

    public void removeUidsInRange(int startUid, int endUid) {
        if (mErrors > ERROR_THRESHOLD) {
            return;
        }
        mWriteLock.lock();
        int firstIndex = mData.indexOfKey(startUid);
        int lastIndex = mData.indexOfKey(endUid);
        mData.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
        mWriteLock.unlock();
    }

    public BpfMapIterator open() {
        return open(false);
    }

    public BpfMapIterator open(boolean ignoreCache) {
        if (mErrors > ERROR_THRESHOLD) {
            return null;
        }
        if (!mTracking && !startTrackingBpfTimes()) {
            Slog.w(mTag, "Failed to start tracking");
            mErrors++;
            return null;
        }
        if (ignoreCache) {
            mWriteLock.lock();
        } else {
            mReadLock.lock();
            if (dataValid()) {
                return new BpfMapIterator();
            }
            mReadLock.unlock();
            mWriteLock.lock();
            if (dataValid()) {
                mReadLock.lock();
                mWriteLock.unlock();
                return new BpfMapIterator();
            }
        }
        if (readBpfData()) {
            mLastReadTime = SystemClock.elapsedRealtime();
            mReadLock.lock();
            mWriteLock.unlock();
            return new BpfMapIterator();
        }

        mWriteLock.unlock();
        mErrors++;
        Slog.w(mTag, "Failed to read bpf times");
        return null;
    }

    private boolean dataValid() {
        return mData.size() > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS_MS);
    }

    public class BpfMapIterator implements AutoCloseable {
        private int mPos;

        public BpfMapIterator() {
        };

        public boolean getNextUid(long[] buf) {
            if (mPos >= mData.size()) {
                return false;
            }
            buf[0] = mData.keyAt(mPos);
            System.arraycopy(mData.valueAt(mPos), 0, buf, 1, mData.valueAt(mPos).length);
            mPos++;
            return true;
        }

        public void close() {
            mReadLock.unlock();
        }
    }

    public static class KernelCpuUidFreqTimeBpfMapReader extends KernelCpuUidBpfMapReader {

        private final native boolean removeUidRange(int startUid, int endUid);

        @Override
        protected final native boolean readBpfData();

        @Override
        public final native long[] getDataDimensions();

        @Override
        public void removeUidsInRange(int startUid, int endUid) {
            mWriteLock.lock();
            super.removeUidsInRange(startUid, endUid);
            removeUidRange(startUid, endUid);
            mWriteLock.unlock();
        }
    }

    public static class KernelCpuUidActiveTimeBpfMapReader extends KernelCpuUidBpfMapReader {

        @Override
        protected final native boolean readBpfData();

        @Override
        public final native long[] getDataDimensions();
    }

    public static class KernelCpuUidClusterTimeBpfMapReader extends KernelCpuUidBpfMapReader {

        @Override
        protected final native boolean readBpfData();

        @Override
        public final native long[] getDataDimensions();
    }
}
+264 −91
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;

import java.io.BufferedReader;
import java.io.FileWriter;
@@ -57,6 +58,8 @@ public abstract class KernelCpuUidTimeReader<T> {
    final SparseArray<T> mLastTimes = new SparseArray<>();
    final KernelCpuProcStringReader mReader;
    final boolean mThrottle;
    protected boolean mBpfTimesAvailable;
    final KernelCpuUidBpfMapReader mBpfReader;
    private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
    private long mLastReadTimeMs = 0;

@@ -73,9 +76,15 @@ public abstract class KernelCpuUidTimeReader<T> {
        void onUidCpuTime(int uid, T time);
    }

    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
        mReader = reader;
        mThrottle = throttle;
        mBpfReader = bpfReader;
        mBpfTimesAvailable = (mBpfReader != null);
    }

    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
        this(reader, null, throttle);
    }

    /**
@@ -151,9 +160,13 @@ public abstract class KernelCpuUidTimeReader<T> {
        }
        mLastTimes.put(startUid, null);
        mLastTimes.put(endUid, null);
        final int firstIndex = mLastTimes.indexOfKey(startUid);
        final int lastIndex = mLastTimes.indexOfKey(endUid);
        int firstIndex = mLastTimes.indexOfKey(startUid);
        int lastIndex = mLastTimes.indexOfKey(endUid);
        mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);

        if (mBpfTimesAvailable) {
            mBpfReader.removeUidsInRange(startUid, endUid);
        }
    }

    /**
@@ -323,13 +336,13 @@ public abstract class KernelCpuUidTimeReader<T> {

        public KernelCpuUidFreqTimeReader(boolean throttle) {
            this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
                    throttle);
                 KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
        }

        @VisibleForTesting
        public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
                boolean throttle) {
            super(reader, throttle);
                KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
            super(reader, bpfReader, throttle);
            mProcFilePath = Paths.get(procFile);
        }

@@ -370,6 +383,10 @@ public abstract class KernelCpuUidTimeReader<T> {
            if (!mAllUidTimesAvailable) {
                return null;
            }
            if (mBpfTimesAvailable) {
                readFreqsThroughBpf();
            }
            if (mCpuFreqs == null) {
                final int oldMask = StrictMode.allowThreadDiskReadsMask();
                try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
                    if (readFreqs(reader.readLine()) == null) {
@@ -384,6 +401,7 @@ public abstract class KernelCpuUidTimeReader<T> {
                } finally {
                    StrictMode.setThreadPolicyMask(oldMask);
                }
            }
            // Check if the freqs in the proc file correspond to per-cluster freqs.
            final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
            final int numClusters = powerProfile.getNumCpuClusters();
@@ -402,6 +420,21 @@ public abstract class KernelCpuUidTimeReader<T> {
            return mCpuFreqs;
        }

        private long[] readFreqsThroughBpf() {
            if (!mBpfTimesAvailable || mBpfReader == null) {
                return null;
            }
            mCpuFreqs = mBpfReader.getDataDimensions();
            if (mCpuFreqs == null) {
                return null;
            }
            mFreqCount = mCpuFreqs.length;
            mCurTimes = new long[mFreqCount];
            mDeltaTimes = new long[mFreqCount];
            mBuffer = new long[mFreqCount + 1];
            return mCpuFreqs;
        }

        private long[] readFreqs(String line) {
            if (line == null) {
                return null;
@@ -422,18 +455,7 @@ public abstract class KernelCpuUidTimeReader<T> {
            return mCpuFreqs;
        }

        @Override
        void readDeltaImpl(@Nullable Callback<long[]> cb) {
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
                }
                CharBuffer buf;
                while ((buf = iter.nextLine()) != null) {
                    if (asLongs(buf, mBuffer) != mBuffer.length) {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
        private void processUidDelta(@Nullable Callback<long[]> cb) {
            final int uid = (int) mBuffer[0];
            long[] lastTimes = mLastTimes.get(uid);
            if (lastTimes == null) {
@@ -459,11 +481,47 @@ public abstract class KernelCpuUidTimeReader<T> {
                }
            }
        }

        @Override
        void readDeltaImpl(@Nullable Callback<long[]> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            processUidDelta(cb);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
                }
                CharBuffer buf;
                while ((buf = iter.nextLine()) != null) {
                    if (asLongs(buf, mBuffer) != mBuffer.length) {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
                    processUidDelta(cb);
                }
            }
        }

        @Override
        void readAbsoluteImpl(Callback<long[]> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            copyToCurTimes();
                            cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
@@ -481,11 +539,24 @@ public abstract class KernelCpuUidTimeReader<T> {
        }

        private void copyToCurTimes() {
            long factor = mBpfTimesAvailable ? 1 : 10;
            for (int i = 0; i < mFreqCount; i++) {
                mCurTimes[i] = mBuffer[i + 1] * 10;
                mCurTimes[i] = mBuffer[i + 1] * factor;
            }
        }

        private boolean checkPrecondition(BpfMapIterator iter) {
            if (iter == null) {
                mBpfTimesAvailable = false;
                return false;
            }
            if (mCpuFreqs != null) {
                return true;
            }
            mBpfTimesAvailable = (readFreqsThroughBpf() != null);
            return mBpfTimesAvailable;
        }

        private boolean checkPrecondition(ProcFileIterator iter) {
            if (iter == null || !iter.hasNextLine()) {
                // Error logged in KernelCpuProcStringReader.
@@ -544,16 +615,43 @@ public abstract class KernelCpuUidTimeReader<T> {
        private long[] mBuffer;

        public KernelCpuUidActiveTimeReader(boolean throttle) {
            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
                  KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
        }

        @VisibleForTesting
        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
            super(reader, throttle);
        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
            super(reader, bpfReader, throttle);
        }

        private void processUidDelta(@Nullable Callback<Long> cb) {
            int uid = (int) mBuffer[0];
            long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
            if (cpuActiveTime > 0) {
                long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
                if (delta > 0) {
                    mLastTimes.put(uid, cpuActiveTime);
                    if (cb != null) {
                        cb.onUidCpuTime(uid, delta);
                    }
                } else if (delta < 0) {
                    Slog.e(mTag, "Negative delta from active time proc: " + delta);
                }
            }
        }

        @Override
        void readDeltaImpl(@Nullable Callback<Long> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            processUidDelta(cb);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
@@ -564,25 +662,30 @@ public abstract class KernelCpuUidTimeReader<T> {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
                    int uid = (int) mBuffer[0];
                    long cpuActiveTime = sumActiveTime(mBuffer);
                    if (cpuActiveTime > 0) {
                        long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
                        if (delta > 0) {
                            mLastTimes.put(uid, cpuActiveTime);
                            if (cb != null) {
                                cb.onUidCpuTime(uid, delta);
                            }
                        } else if (delta < 0) {
                            Slog.e(mTag, "Negative delta from active time proc: " + delta);
                    processUidDelta(cb);
                }
            }
        }

        private void processUidAbsolute(@Nullable Callback<Long> cb) {
            long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
            if (cpuActiveTime > 0) {
                cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
            }
        }

        @Override
        void readAbsoluteImpl(Callback<Long> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            processUidAbsolute(cb);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
@@ -593,23 +696,38 @@ public abstract class KernelCpuUidTimeReader<T> {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
                    long cpuActiveTime = sumActiveTime(mBuffer);
                    if (cpuActiveTime > 0) {
                        cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
                    }
                    processUidAbsolute(cb);
                }
            }
        }

        private static long sumActiveTime(long[] times) {
        private static long sumActiveTime(long[] times, double factor) {
            // UID is stored at times[0].
            double sum = 0;
            for (int i = 1; i < times.length; i++) {
                sum += (double) times[i] * 10 / i; // Unit is 10ms.
                sum += (double) times[i] * factor / i; // Unit is 10ms.
            }
            return (long) sum;
        }

        private boolean checkPrecondition(BpfMapIterator iter) {
            if (iter == null) {
                mBpfTimesAvailable = false;
                return false;
            }
            if (mCores > 0) {
                return true;
            }
            long[] cores = mBpfReader.getDataDimensions();
            if (cores == null || cores.length < 1) {
                mBpfTimesAvailable = false;
                return false;
            }
            mCores = (int) cores[0];
            mBuffer = new long[mCores + 1];
            return true;
        }

        private boolean checkPrecondition(ProcFileIterator iter) {
            if (iter == null || !iter.hasNextLine()) {
                // Error logged in KernelCpuProcStringReader.
@@ -664,26 +782,17 @@ public abstract class KernelCpuUidTimeReader<T> {
        private long[] mDeltaTime;

        public KernelCpuUidClusterTimeReader(boolean throttle) {
            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
                  KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
        }

        @VisibleForTesting
        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
            super(reader, throttle);
        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
                                             KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
            super(reader, bpfReader, throttle);
        }

        @Override
        void readDeltaImpl(@Nullable Callback<long[]> cb) {
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
                }
                CharBuffer buf;
                while ((buf = iter.nextLine()) != null) {
                    if (asLongs(buf, mBuffer) != mBuffer.length) {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
        void processUidDelta(@Nullable Callback<long[]> cb) {
            int uid = (int) mBuffer[0];
            long[] lastTimes = mLastTimes.get(uid);
            if (lastTimes == null) {
@@ -708,11 +817,47 @@ public abstract class KernelCpuUidTimeReader<T> {
                }
            }
        }

        @Override
        void readDeltaImpl(@Nullable Callback<long[]> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            processUidDelta(cb);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
                }
                CharBuffer buf;
                while ((buf = iter.nextLine()) != null) {
                    if (asLongs(buf, mBuffer) != mBuffer.length) {
                        Slog.wtf(mTag, "Invalid line: " + buf.toString());
                        continue;
                    }
                    processUidDelta(cb);
                }
            }
        }

        @Override
        void readAbsoluteImpl(Callback<long[]> cb) {
            if (mBpfTimesAvailable) {
                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
                    if (checkPrecondition(iter)) {
                        while (iter.getNextUid(mBuffer)) {
                            sumClusterTime();
                            cb.onUidCpuTime((int) mBuffer[0], mCurTime);
                        }
                        return;
                    }
                }
            }
            try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                if (!checkPrecondition(iter)) {
                    return;
@@ -730,17 +875,45 @@ public abstract class KernelCpuUidTimeReader<T> {
        }

        private void sumClusterTime() {
            double factor = mBpfTimesAvailable ? 1 : 10;
            // UID is stored at mBuffer[0].
            int core = 1;
            for (int i = 0; i < mNumClusters; i++) {
                double sum = 0;
                for (int j = 1; j <= mCoresOnClusters[i]; j++) {
                    sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
                    sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
                }
                mCurTime[i] = (long) sum;
            }
        }

        private boolean checkPrecondition(BpfMapIterator iter) {
            if (iter == null) {
                mBpfTimesAvailable = false;
                return false;
            }
            if (mNumClusters > 0) {
                return true;
            }
            long[] coresOnClusters = mBpfReader.getDataDimensions();
            if (coresOnClusters == null || coresOnClusters.length < 1) {
                mBpfTimesAvailable = false;
                return false;
            }
            mNumClusters = coresOnClusters.length;
            mCoresOnClusters = new int[mNumClusters];
            int cores = 0;
            for (int i = 0; i < mNumClusters; i++) {
                mCoresOnClusters[i] = (int) coresOnClusters[i];
                cores += mCoresOnClusters[i];
            }
            mNumCores = cores;
            mBuffer = new long[cores + 1];
            mCurTime = new long[mNumClusters];
            mDeltaTime = new long[mNumClusters];
            return true;
        }

        private boolean checkPrecondition(ProcFileIterator iter) {
            if (iter == null || !iter.hasNextLine()) {
                // Error logged in KernelCpuProcStringReader.
+18 −0

File changed.

Preview size limit exceeded, changes collapsed.

+3 −0
Original line number Diff line number Diff line
@@ -207,6 +207,8 @@ cc_library_shared {
        "com_android_internal_os_AtomicDirectory.cpp",
        "com_android_internal_os_ClassLoaderFactory.cpp",
        "com_android_internal_os_FuseAppLoop.cpp",
        "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
        "com_android_internal_os_KernelSingleUidTimeReader.cpp",
        "com_android_internal_os_Zygote.cpp",
        "com_android_internal_os_ZygoteInit.cpp",
        "com_android_internal_util_VirtualRefBasePtr.cpp",
@@ -303,6 +305,7 @@ cc_library_shared {
        "libdl",
        "libdl_android",
        "libstatslog",
        "libtimeinstate",
        "server_configurable_flags",
    ],

+4 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading