1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.messaging.datamodel.media; 17 18 import android.os.SystemClock; 19 20 import com.android.messaging.util.Assert; 21 import com.android.messaging.util.LogUtil; 22 import com.google.common.base.Throwables; 23 24 import java.util.ArrayList; 25 import java.util.concurrent.locks.ReentrantLock; 26 27 /** 28 * A ref-counted class that holds loaded media resource, be it bitmaps or media bytes. 29 * Subclasses must implement the close() method to release any resources (such as bitmaps) 30 * when it's no longer used. 31 * 32 * Instances of the subclasses are: 33 * 1. Loaded by their corresponding MediaRequest classes. 34 * 2. Maintained by MediaResourceManager in its MediaCache pool. 35 * 3. Used by the UI (such as ContactIconViews) to present the content. 36 * 37 * Note: all synchronized methods in this class (e.g. addRef()) should not attempt to make outgoing 38 * calls that could potentially acquire media cache locks due to the potential deadlock this can 39 * cause. To synchronize read/write access to shared resource, {@link #acquireLock()} and 40 * {@link #releaseLock()} must be used, instead of using synchronized keyword. 41 */ 42 public abstract class RefCountedMediaResource { 43 private final String mKey; 44 private int mRef = 0; 45 private long mLastRefAddTimestamp; 46 47 // Set DEBUG to true to enable detailed stack trace for each addRef() and release() operation 48 // to find out where each ref change happens. 49 private static final boolean DEBUG = false; 50 private static final String TAG = "bugle_media_ref_history"; 51 private final ArrayList<String> mRefHistory = new ArrayList<String>(); 52 53 // A lock that guards access to shared members in this class (and all its subclasses). 54 private final ReentrantLock mLock = new ReentrantLock(); 55 RefCountedMediaResource(final String key)56 public RefCountedMediaResource(final String key) { 57 mKey = key; 58 } 59 getKey()60 public String getKey() { 61 return mKey; 62 } 63 addRef()64 public void addRef() { 65 acquireLock(); 66 try { 67 if (DEBUG) { 68 mRefHistory.add("Added ref current ref = " + mRef); 69 mRefHistory.add(Throwables.getStackTraceAsString(new Exception())); 70 } 71 72 mRef++; 73 mLastRefAddTimestamp = SystemClock.elapsedRealtime(); 74 } finally { 75 releaseLock(); 76 } 77 } 78 release()79 public void release() { 80 acquireLock(); 81 try { 82 if (DEBUG) { 83 mRefHistory.add("Released ref current ref = " + mRef); 84 mRefHistory.add(Throwables.getStackTraceAsString(new Exception())); 85 } 86 87 mRef--; 88 if (mRef == 0) { 89 close(); 90 } else if (mRef < 0) { 91 if (DEBUG) { 92 LogUtil.i(TAG, "Unwinding ref count history for RefCountedMediaResource " 93 + this); 94 for (final String ref : mRefHistory) { 95 LogUtil.i(TAG, ref); 96 } 97 } 98 Assert.fail("RefCountedMediaResource has unbalanced ref. Refcount=" + mRef); 99 } 100 } finally { 101 releaseLock(); 102 } 103 } 104 getRefCount()105 public int getRefCount() { 106 acquireLock(); 107 try { 108 return mRef; 109 } finally { 110 releaseLock(); 111 } 112 } 113 getLastRefAddTimestamp()114 public long getLastRefAddTimestamp() { 115 acquireLock(); 116 try { 117 return mLastRefAddTimestamp; 118 } finally { 119 releaseLock(); 120 } 121 } 122 assertSingularRefCount()123 public void assertSingularRefCount() { 124 acquireLock(); 125 try { 126 Assert.equals(1, mRef); 127 } finally { 128 releaseLock(); 129 } 130 } 131 acquireLock()132 void acquireLock() { 133 mLock.lock(); 134 } 135 releaseLock()136 void releaseLock() { 137 mLock.unlock(); 138 } 139 assertLockHeldByCurrentThread()140 void assertLockHeldByCurrentThread() { 141 Assert.isTrue(mLock.isHeldByCurrentThread()); 142 } 143 isEncoded()144 boolean isEncoded() { 145 return false; 146 } 147 isCacheable()148 boolean isCacheable() { 149 return true; 150 } 151 getMediaDecodingRequest( final MediaRequest<? extends RefCountedMediaResource> originalRequest)152 MediaRequest<? extends RefCountedMediaResource> getMediaDecodingRequest( 153 final MediaRequest<? extends RefCountedMediaResource> originalRequest) { 154 return null; 155 } 156 getMediaEncodingRequest( final MediaRequest<? extends RefCountedMediaResource> originalRequest)157 MediaRequest<? extends RefCountedMediaResource> getMediaEncodingRequest( 158 final MediaRequest<? extends RefCountedMediaResource> originalRequest) { 159 return null; 160 } 161 getMediaSize()162 public abstract int getMediaSize(); close()163 protected abstract void close(); 164 }