1 /* 2 * Copyright (C) 2013 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 17 package com.android.camera.app; 18 19 import android.app.ActivityManager; 20 import android.content.ComponentCallbacks2; 21 import android.content.Context; 22 import android.content.res.Configuration; 23 24 import com.android.camera.app.MediaSaver.QueueListener; 25 import com.android.camera.debug.Log; 26 import com.android.camera.util.AndroidServices; 27 import com.android.camera.util.GservicesHelper; 28 29 import java.util.HashMap; 30 import java.util.LinkedList; 31 32 /** 33 * Default implementation of the {@link MemoryManager}. 34 * <p> 35 * TODO: Add GCam signals. 36 */ 37 public class MemoryManagerImpl implements MemoryManager, QueueListener, ComponentCallbacks2 { 38 private static final Log.Tag TAG = new Log.Tag("MemoryManagerImpl"); 39 /** 40 * Let's signal only 70% of max memory is allowed to be used by native code 41 * to allow a buffer for special captures. 42 */ 43 private static final float MAX_MEM_ALLOWED = 0.70f; 44 45 private static final int[] sCriticalStates = new int[] { 46 ComponentCallbacks2.TRIM_MEMORY_COMPLETE, 47 ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL 48 }; 49 50 private final LinkedList<MemoryListener> mListeners = new LinkedList<MemoryListener>(); 51 52 /** 53 * The maximum amount of memory allowed to be allocated in native code (in 54 * megabytes) 55 */ 56 private final int mMaxAllowedNativeMemory; 57 58 /** 59 * Used to query a breakdown of current memory consumption and memory 60 * thresholds. 61 */ 62 private final MemoryQuery mMemoryQuery; 63 64 /** 65 * Use this to create a wired-up memory manager. 66 * 67 * @param context this is used to register for system memory events. 68 * @param mediaSaver this used to check if the saving queue is full. 69 * @return A wired-up memory manager instance. 70 */ create(Context context, MediaSaver mediaSaver)71 public static MemoryManagerImpl create(Context context, MediaSaver mediaSaver) { 72 ActivityManager activityManager = AndroidServices.instance().provideActivityManager(); 73 int maxAllowedNativeMemory = getMaxAllowedNativeMemory(context); 74 MemoryQuery mMemoryQuery = new MemoryQuery(activityManager); 75 MemoryManagerImpl memoryManager = new MemoryManagerImpl(maxAllowedNativeMemory, 76 mMemoryQuery); 77 context.registerComponentCallbacks(memoryManager); 78 mediaSaver.setQueueListener(memoryManager); 79 return memoryManager; 80 } 81 82 /** 83 * Use {@link #create(Context, MediaSaver)} to make sure it's wired up 84 * correctly. 85 */ MemoryManagerImpl(int maxAllowedNativeMemory, MemoryQuery memoryQuery)86 private MemoryManagerImpl(int maxAllowedNativeMemory, MemoryQuery memoryQuery) { 87 mMaxAllowedNativeMemory = maxAllowedNativeMemory; 88 mMemoryQuery = memoryQuery; 89 Log.d(TAG, "Max native memory: " + mMaxAllowedNativeMemory + " MB"); 90 91 } 92 93 @Override addListener(MemoryListener listener)94 public void addListener(MemoryListener listener) { 95 synchronized (mListeners) { 96 if (!mListeners.contains(listener)) { 97 mListeners.add(listener); 98 } else { 99 Log.w(TAG, "Listener already added."); 100 } 101 } 102 } 103 104 @Override removeListener(MemoryListener listener)105 public void removeListener(MemoryListener listener) { 106 synchronized (mListeners) { 107 if (mListeners.contains(listener)) { 108 mListeners.remove(listener); 109 } else { 110 Log.w(TAG, "Cannot remove listener that was never added."); 111 } 112 } 113 } 114 115 @Override onConfigurationChanged(Configuration newConfig)116 public void onConfigurationChanged(Configuration newConfig) { 117 } 118 119 @Override onLowMemory()120 public void onLowMemory() { 121 notifyLowMemory(); 122 } 123 124 @Override onTrimMemory(int level)125 public void onTrimMemory(int level) { 126 for (int i = 0; i < sCriticalStates.length; ++i) { 127 if (level == sCriticalStates[i]) { 128 notifyLowMemory(); 129 return; 130 } 131 } 132 } 133 134 @Override onQueueStatus(boolean full)135 public void onQueueStatus(boolean full) { 136 notifyCaptureStateUpdate(full ? STATE_LOW_MEMORY : STATE_OK); 137 } 138 139 @Override getMaxAllowedNativeMemoryAllocation()140 public int getMaxAllowedNativeMemoryAllocation() { 141 return mMaxAllowedNativeMemory; 142 } 143 144 @Override queryMemory()145 public HashMap queryMemory() { 146 return mMemoryQuery.queryMemory(); 147 } 148 149 /** Helper to determine max allowed native memory allocation (in megabytes). */ getMaxAllowedNativeMemory(Context context)150 private static int getMaxAllowedNativeMemory(Context context) { 151 // First check whether we have a system override. 152 int maxAllowedOverrideMb = GservicesHelper.getMaxAllowedNativeMemoryMb(context 153 .getContentResolver()); 154 if (maxAllowedOverrideMb > 0) { 155 Log.d(TAG, "Max native memory overridden: " + maxAllowedOverrideMb); 156 return maxAllowedOverrideMb; 157 } 158 159 ActivityManager activityManager = AndroidServices.instance().provideActivityManager(); 160 161 // Use the max of the regular memory class and the large memory class. 162 // This is defined as the maximum memory allowed to be used by the 163 // Dalvik heap, but it's safe to assume the app can use the same amount 164 // once more in native code. 165 return (int) (Math.max(activityManager.getMemoryClass(), 166 activityManager.getLargeMemoryClass()) * MAX_MEM_ALLOWED); 167 } 168 169 /** Notify our listener that memory is running low. */ notifyLowMemory()170 private void notifyLowMemory() { 171 synchronized (mListeners) { 172 for (MemoryListener listener : mListeners) { 173 listener.onLowMemory(); 174 } 175 } 176 } 177 notifyCaptureStateUpdate(int captureState)178 private void notifyCaptureStateUpdate(int captureState) { 179 synchronized (mListeners) { 180 for (MemoryListener listener : mListeners) { 181 listener.onMemoryStateChanged(captureState); 182 } 183 } 184 } 185 } 186