Home | History | Annotate | Download | only in app
      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.GservicesHelper;
     27 
     28 import java.util.HashMap;
     29 import java.util.LinkedList;
     30 
     31 /**
     32  * Default implementation of the {@link MemoryManager}.
     33  * <p>
     34  * TODO: Add GCam signals.
     35  */
     36 public class MemoryManagerImpl implements MemoryManager, QueueListener, ComponentCallbacks2 {
     37     private static final Log.Tag TAG = new Log.Tag("MemoryManagerImpl");
     38     /**
     39      * Let's signal only 70% of max memory is allowed to be used by native code
     40      * to allow a buffer for special captures.
     41      */
     42     private static final float MAX_MEM_ALLOWED = 0.70f;
     43 
     44     private static final int[] sCriticalStates = new int[] {
     45             ComponentCallbacks2.TRIM_MEMORY_COMPLETE,
     46             ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
     47     };
     48 
     49     private final LinkedList<MemoryListener> mListeners = new LinkedList<MemoryListener>();
     50 
     51     /**
     52      * The maximum amount of memory allowed to be allocated in native code (in
     53      * megabytes)
     54      */
     55     private final int mMaxAllowedNativeMemory;
     56 
     57     /**
     58      * Used to query a breakdown of current memory consumption and memory
     59      * thresholds.
     60      */
     61     private final MemoryQuery mMemoryQuery;
     62 
     63     /**
     64      * Use this to create a wired-up memory manager.
     65      *
     66      * @param context this is used to register for system memory events.
     67      * @param mediaSaver this used to check if the saving queue is full.
     68      * @return A wired-up memory manager instance.
     69      */
     70     public static MemoryManagerImpl create(Context context, MediaSaver mediaSaver) {
     71         ActivityManager activityManager =
     72                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
     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      */
     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
     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
    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
    116     public void onConfigurationChanged(Configuration newConfig) {
    117     }
    118 
    119     @Override
    120     public void onLowMemory() {
    121         notifyLowMemory();
    122     }
    123 
    124     @Override
    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
    135     public void onQueueStatus(boolean full) {
    136         notifyCaptureStateUpdate(full ? STATE_LOW_MEMORY : STATE_OK);
    137     }
    138 
    139     @Override
    140     public int getMaxAllowedNativeMemoryAllocation() {
    141         return mMaxAllowedNativeMemory;
    142     }
    143 
    144     @Override
    145     public HashMap queryMemory() {
    146         return mMemoryQuery.queryMemory();
    147     }
    148 
    149     /** Helper to determine max allowed native memory allocation (in megabytes). */
    150     private static int getMaxAllowedNativeMemory(Context context) {
    151         // First check whether we have a system override.
    152         int maxAllowedOverrideMb = GservicesHelper.getMaxAllowedNativeMemoryMb(context);
    153         if (maxAllowedOverrideMb > 0) {
    154             Log.d(TAG, "Max native memory overridden: " + maxAllowedOverrideMb);
    155             return maxAllowedOverrideMb;
    156         }
    157 
    158         ActivityManager activityManager = (ActivityManager) context
    159                 .getSystemService(Context.ACTIVITY_SERVICE);
    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. */
    170     private void notifyLowMemory() {
    171         synchronized (mListeners) {
    172             for (MemoryListener listener : mListeners) {
    173                 listener.onLowMemory();
    174             }
    175         }
    176     }
    177 
    178     private void notifyCaptureStateUpdate(int captureState) {
    179         synchronized (mListeners) {
    180             for (MemoryListener listener : mListeners) {
    181                 listener.onMemoryStateChanged(captureState);
    182             }
    183         }
    184     }
    185 }
    186