130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni/*
230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Copyright (C) 2011 The Android Open Source Project
330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Licensed under the Apache License, Version 2.0 (the "License");
530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * you may not use this file except in compliance with the License.
630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * You may obtain a copy of the License at
730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *      http://www.apache.org/licenses/LICENSE-2.0
930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
1030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Unless required by applicable law or agreed to in writing, software
1130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * distributed under the License is distributed on an "AS IS" BASIS,
1230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * See the License for the specific language governing permissions and
1430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * limitations under the License.
1530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni */
1630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceronipackage android.filterfw.core;
1930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
2030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Frame;
2130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.FrameFormat;
2230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.SimpleFrameManager;
2330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
24489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Rennimport java.util.Map;
25489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Rennimport java.util.SortedMap;
26489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Rennimport java.util.TreeMap;
27489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn
28a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala/**
29a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala * @hide
30a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala */
3130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceronipublic class CachedFrameManager extends SimpleFrameManager {
3230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
33489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn    private SortedMap<Integer, Frame> mAvailableFrames;
34489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn    private int mStorageCapacity = 24 * 1024 * 1024; // Cap default storage to 24MB
3530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private int mStorageSize = 0;
36489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn    private int mTimeStamp = 0;
3730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
3830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public CachedFrameManager() {
3930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        super();
40489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn        mAvailableFrames = new TreeMap<Integer, Frame>();
4130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
4230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
4333d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
4430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public Frame newFrame(FrameFormat format) {
4530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        Frame result = findAvailableFrame(format, Frame.NO_BINDING, 0);
4630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (result == null) {
4730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            result = super.newFrame(format);
4830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
495b9eb6d686c439545dafcc8e25e9e3747281c3deEino-Ville Talvala        result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
5030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        return result;
5130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
5230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
5333d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
5430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) {
5530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        Frame result = findAvailableFrame(format, bindingType, bindingId);
5630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (result == null) {
5730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            result = super.newBoundFrame(format, bindingType, bindingId);
5830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
595b9eb6d686c439545dafcc8e25e9e3747281c3deEino-Ville Talvala        result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
6030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        return result;
6130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
6230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
6333d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
6430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public Frame retainFrame(Frame frame) {
6530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        return super.retainFrame(frame);
6630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
6730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
6833d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
6930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public Frame releaseFrame(Frame frame) {
7030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (frame.isReusable()) {
7130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            int refCount = frame.decRefCount();
72f5ae8eafa7605c6593f62f873b62cb64a3254db3Marius Renn            if (refCount == 0 && frame.hasNativeAllocation()) {
7330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                if (!storeFrame(frame)) {
74f5ae8eafa7605c6593f62f873b62cb64a3254db3Marius Renn                    frame.releaseNativeAllocation();
7530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                }
7630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                return null;
7730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            } else if (refCount < 0) {
7830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                throw new RuntimeException("Frame reference count dropped below 0!");
7930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            }
8030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        } else {
8130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            super.releaseFrame(frame);
8230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
8330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        return frame;
8430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
8530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
86776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn    public void clearCache() {
87776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn        for (Frame frame : mAvailableFrames.values()) {
88776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn            frame.releaseNativeAllocation();
89776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn        }
90776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn        mAvailableFrames.clear();
91776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn    }
92776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn
93776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn    @Override
94776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn    public void tearDown() {
95776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn        clearCache();
96776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn    }
97776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn
9830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private boolean storeFrame(Frame frame) {
9930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        synchronized(mAvailableFrames) {
100489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            // Make sure this frame alone does not exceed capacity
101489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            int frameSize = frame.getFormat().getSize();
102489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            if (frameSize > mStorageCapacity) {
103489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn                return false;
10430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            }
105489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn
106489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            // Drop frames if adding this frame would exceed capacity
107489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            int newStorageSize = mStorageSize + frameSize;
108489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            while (newStorageSize > mStorageCapacity) {
109489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn                dropOldestFrame();
110489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn                newStorageSize = mStorageSize + frameSize;
111489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            }
112489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn
113489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            // Store new frame
11492568796d043c794553e5bcaa797c906899e71f0Marius Renn            frame.onFrameStore();
115489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            mStorageSize = newStorageSize;
116489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            mAvailableFrames.put(mTimeStamp, frame);
117489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            ++mTimeStamp;
118489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            return true;
11930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
12030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
12130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
122489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn    private void dropOldestFrame() {
123489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn        int oldest = mAvailableFrames.firstKey();
124489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn        Frame frame = mAvailableFrames.get(oldest);
125489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn        mStorageSize -= frame.getFormat().getSize();
126f5ae8eafa7605c6593f62f873b62cb64a3254db3Marius Renn        frame.releaseNativeAllocation();
127489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn        mAvailableFrames.remove(oldest);
128489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn    }
129489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn
13030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private Frame findAvailableFrame(FrameFormat format, int bindingType, long bindingId) {
13130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Look for a frame that is compatible with the requested format
13230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        synchronized(mAvailableFrames) {
133489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn            for (Map.Entry<Integer, Frame> entry : mAvailableFrames.entrySet()) {
134489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn                Frame frame = entry.getValue();
13530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                // Check that format is compatible
13630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                if (frame.getFormat().isReplaceableBy(format)) {
13730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    // Check that binding is compatible (if frame is bound)
138776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn                    if ((bindingType == frame.getBindingType())
139776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn                        && (bindingType == Frame.NO_BINDING || bindingId == frame.getBindingId())) {
14030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // We found one! Take it out of the set of available frames and attach the
14130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // requested format to it.
14230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        super.retainFrame(frame);
143489c240d3ae33e83dc62ea6f3cc864e47c0e2e3bMarius Renn                        mAvailableFrames.remove(entry.getKey());
14492568796d043c794553e5bcaa797c906899e71f0Marius Renn                        frame.onFrameFetch();
14530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        frame.reset(format);
14630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        mStorageSize -= format.getSize();
14730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        return frame;
14830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    }
14930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                }
15030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            }
15130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
15230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        return null;
15330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
154776102d45a18a5df53d2ec76c5d93f20b3e99da1Marius Renn
15530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni}
156