blob: c21f4774b5b3fe3143d9007e1e24a25e12af7ddf [file] [log] [blame]
/*
* Copyright (C) 2015 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.messaging.datamodel.media;
import android.os.SystemClock;
import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil;
import com.google.common.base.Throwables;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
/**
* A ref-counted class that holds loaded media resource, be it bitmaps or media bytes.
* Subclasses must implement the close() method to release any resources (such as bitmaps)
* when it's no longer used.
*
* Instances of the subclasses are:
* 1. Loaded by their corresponding MediaRequest classes.
* 2. Maintained by MediaResourceManager in its MediaCache pool.
* 3. Used by the UI (such as ContactIconViews) to present the content.
*
* Note: all synchronized methods in this class (e.g. addRef()) should not attempt to make outgoing
* calls that could potentially acquire media cache locks due to the potential deadlock this can
* cause. To synchronize read/write access to shared resource, {@link #acquireLock()} and
* {@link #releaseLock()} must be used, instead of using synchronized keyword.
*/
public abstract class RefCountedMediaResource {
private final String mKey;
private int mRef = 0;
private long mLastRefAddTimestamp;
// Set DEBUG to true to enable detailed stack trace for each addRef() and release() operation
// to find out where each ref change happens.
private static final boolean DEBUG = false;
private static final String TAG = "bugle_media_ref_history";
private final ArrayList<String> mRefHistory = new ArrayList<String>();
// A lock that guards access to shared members in this class (and all its subclasses).
private final ReentrantLock mLock = new ReentrantLock();
public RefCountedMediaResource(final String key) {
mKey = key;
}
public String getKey() {
return mKey;
}
public void addRef() {
acquireLock();
try {
if (DEBUG) {
mRefHistory.add("Added ref current ref = " + mRef);
mRefHistory.add(Throwables.getStackTraceAsString(new Exception()));
}
mRef++;
mLastRefAddTimestamp = SystemClock.elapsedRealtime();
} finally {
releaseLock();
}
}
public void release() {
acquireLock();
try {
if (DEBUG) {
mRefHistory.add("Released ref current ref = " + mRef);
mRefHistory.add(Throwables.getStackTraceAsString(new Exception()));
}
mRef--;
if (mRef == 0) {
close();
} else if (mRef < 0) {
if (DEBUG) {
LogUtil.i(TAG, "Unwinding ref count history for RefCountedMediaResource "
+ this);
for (final String ref : mRefHistory) {
LogUtil.i(TAG, ref);
}
}
Assert.fail("RefCountedMediaResource has unbalanced ref. Refcount=" + mRef);
}
} finally {
releaseLock();
}
}
public int getRefCount() {
acquireLock();
try {
return mRef;
} finally {
releaseLock();
}
}
public long getLastRefAddTimestamp() {
acquireLock();
try {
return mLastRefAddTimestamp;
} finally {
releaseLock();
}
}
public void assertSingularRefCount() {
acquireLock();
try {
Assert.equals(1, mRef);
} finally {
releaseLock();
}
}
void acquireLock() {
mLock.lock();
}
void releaseLock() {
mLock.unlock();
}
void assertLockHeldByCurrentThread() {
Assert.isTrue(mLock.isHeldByCurrentThread());
}
boolean isEncoded() {
return false;
}
boolean isCacheable() {
return true;
}
MediaRequest<? extends RefCountedMediaResource> getMediaDecodingRequest(
final MediaRequest<? extends RefCountedMediaResource> originalRequest) {
return null;
}
MediaRequest<? extends RefCountedMediaResource> getMediaEncodingRequest(
final MediaRequest<? extends RefCountedMediaResource> originalRequest) {
return null;
}
public abstract int getMediaSize();
protected abstract void close();
}