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 final LinkedList<MemoryListener> mListeners = new LinkedList<MemoryListener>(); 46 47 /** 48 * The maximum amount of memory allowed to be allocated in native code (in 49 * megabytes) 50 */ 51 private final int mMaxAllowedNativeMemory; 52 53 /** 54 * Used to query a breakdown of current memory consumption and memory 55 * thresholds. 56 */ 57 private final MemoryQuery mMemoryQuery; 58 59 /** 60 * Use this to create a wired-up memory manager. 61 * 62 * @param context this is used to register for system memory events. 63 * @param mediaSaver this used to check if the saving queue is full. 64 * @return A wired-up memory manager instance. 65 */ create(Context context, MediaSaver mediaSaver)66 public static MemoryManagerImpl create(Context context, MediaSaver mediaSaver) { 67 ActivityManager activityManager = AndroidServices.instance().provideActivityManager(); 68 int maxAllowedNativeMemory = getMaxAllowedNativeMemory(context); 69 MemoryQuery mMemoryQuery = new MemoryQuery(activityManager); 70 MemoryManagerImpl memoryManager = new MemoryManagerImpl(maxAllowedNativeMemory, 71 mMemoryQuery); 72 context.registerComponentCallbacks(memoryManager); 73 mediaSaver.setQueueListener(memoryManager); 74 return memoryManager; 75 } 76 77 /** 78 * Use {@link #create(Context, MediaSaver)} to make sure it's wired up 79 * correctly. 80 */ MemoryManagerImpl(int maxAllowedNativeMemory, MemoryQuery memoryQuery)81 private MemoryManagerImpl(int maxAllowedNativeMemory, MemoryQuery memoryQuery) { 82 mMaxAllowedNativeMemory = maxAllowedNativeMemory; 83 mMemoryQuery = memoryQuery; 84 Log.d(TAG, "Max native memory: " + mMaxAllowedNativeMemory + " MB"); 85 86 } 87 88 @Override addListener(MemoryListener listener)89 public void addListener(MemoryListener listener) { 90 synchronized (mListeners) { 91 if (!mListeners.contains(listener)) { 92 mListeners.add(listener); 93 } else { 94 Log.w(TAG, "Listener already added."); 95 } 96 } 97 } 98 99 @Override removeListener(MemoryListener listener)100 public void removeListener(MemoryListener listener) { 101 synchronized (mListeners) { 102 if (mListeners.contains(listener)) { 103 mListeners.remove(listener); 104 } else { 105 Log.w(TAG, "Cannot remove listener that was never added."); 106 } 107 } 108 } 109 110 @Override onConfigurationChanged(Configuration newConfig)111 public void onConfigurationChanged(Configuration newConfig) { 112 } 113 114 @Override onLowMemory()115 public void onLowMemory() { 116 notifyLowMemory(); 117 } 118 119 @Override onTrimMemory(int level)120 public void onTrimMemory(int level) { 121 if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { 122 notifyLowMemory(); 123 } 124 } 125 126 @Override onQueueStatus(boolean full)127 public void onQueueStatus(boolean full) { 128 notifyCaptureStateUpdate(full ? STATE_LOW_MEMORY : STATE_OK); 129 } 130 131 @Override getMaxAllowedNativeMemoryAllocation()132 public int getMaxAllowedNativeMemoryAllocation() { 133 return mMaxAllowedNativeMemory; 134 } 135 136 @Override queryMemory()137 public HashMap queryMemory() { 138 return mMemoryQuery.queryMemory(); 139 } 140 141 /** Helper to determine max allowed native memory allocation (in megabytes). */ getMaxAllowedNativeMemory(Context context)142 private static int getMaxAllowedNativeMemory(Context context) { 143 // First check whether we have a system override. 144 int maxAllowedOverrideMb = GservicesHelper.getMaxAllowedNativeMemoryMb(context 145 .getContentResolver()); 146 if (maxAllowedOverrideMb > 0) { 147 Log.d(TAG, "Max native memory overridden: " + maxAllowedOverrideMb); 148 return maxAllowedOverrideMb; 149 } 150 151 ActivityManager activityManager = AndroidServices.instance().provideActivityManager(); 152 153 // Use the max of the regular memory class and the large memory class. 154 // This is defined as the maximum memory allowed to be used by the 155 // Dalvik heap, but it's safe to assume the app can use the same amount 156 // once more in native code. 157 return (int) (Math.max(activityManager.getMemoryClass(), 158 activityManager.getLargeMemoryClass()) * MAX_MEM_ALLOWED); 159 } 160 161 /** Notify our listener that memory is running low. */ notifyLowMemory()162 private void notifyLowMemory() { 163 synchronized (mListeners) { 164 for (MemoryListener listener : mListeners) { 165 listener.onLowMemory(); 166 } 167 } 168 } 169 notifyCaptureStateUpdate(int captureState)170 private void notifyCaptureStateUpdate(int captureState) { 171 synchronized (mListeners) { 172 for (MemoryListener listener : mListeners) { 173 listener.onMemoryStateChanged(captureState); 174 } 175 } 176 } 177 } 178