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