Can anybody point me at resources with best practice on clearing sensitive runtime images?
Consider a scenario where a sensitive image is downloaded from a server at runtime, loaded into a Bitmap object, and is then displayed in an ImageView in a Fragment.
When the user leaves that screen, or the app is exited/put in the background for a long time, then I want to clear that image data so that it isn't easy to recover.
I was wondering if there is a reliable way to zero out the bitmap data as soon as the Fragment containing the image is destroyed?
This feel tricky to me, as Bitmaps are usually returned as immutable objects, e.g. BitmapFactory.decodeByteArray says:
Decode an immutable bitmap from the specified byte array.
Presumably I would have to create a mutable Bitmap and then copy over its data?
It looks like recycle() won't help me, as that will just mark the data as available for garbage collection, it won't wipe it.
You can simply clear the Bitmap using
someBitmap.eraseColor(android.graphics.Color.TRANSPARENT);
It will fill the bitmap with TRANSPARENT color and erase everything on it.
However, if you have no any references to your bitmap (e.g. you've set null to ImageView that was containing your Bitmap like this
someImageView.setDrawable(null)
the garbage collector should collect it shortly.
Thanks to #IlyaGulya for the eraseColor suggestion. Below is the code I've written so far.
Creating the mutable Bitmap:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inMutable = true;
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, bitmapOptions);
The code to clear the image data in my Fragment (I save the BitmapDrawable into a myBitmapDrawable field when the Fragment receives it):
#Override
public void onDestroy() {
myImageView.setImageDrawable(null);
try {
MyUtils.zeroOutBitmapData(myBitmapDrawable.getBitmap());
} catch (Exception e) {
loggingUtil.logHandledException(e);
}
super.onDestroy();
}
My utility for zeroing out a Bitmap:
public static void zeroOutBitmapData(Bitmap mutableBitmap) {
if (mutableBitmap.isMutable()) {
mutableBitmap.eraseColor(android.graphics.Color.TRANSPARENT);
} else {
logger.error("Expected bitmap to be mutable");
}
}
...and here is a unit test (well, an ApplicationTestCase since I want to test with a real Bitmap):
public void testZeroOutBitmap() throws Exception {
Resources resources = getContext().getResources();
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inMutable = true;
Bitmap mutableBitmap = BitmapFactory.decodeResource(resources, R.drawable.an_example_image);
// Assert that some pixels start out with non zero colors
assertEquals(-789517, mutableBitmap.getPixel(0, 0));
assertEquals(-723724, mutableBitmap.getPixel(10, 10));
MyUtils.zeroOutBitmapData(mutableBitmap);
// Check that pixel data has now been cleared out
assertEquals(android.graphics.Color.TRANSPARENT, mutableBitmap.getPixel(0, 0));
assertEquals(android.graphics.Color.TRANSPARENT, mutableBitmap.getPixel(10, 10));
}
Related
here's the interface:
public interface BitmapCallBackInterface {
void onCallBack(Bitmap bitmap);
}
and here's the method and calling it:
public void downloadImagesFromFireStorage(String imgName, final BitmapCallBackInterface bitmapCallBackInterface) {
StorageReference storageRef = FirebaseStorage.getInstance()
.getReferenceFromUrl("gs://fake-mc-app.appspot.com").child("imgs").child(imgName + ".png");
final long ONE_MEGABYTE = 1024 * 1024;
storageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
bitmapCallBackInterface.onCallBack(bitmap);
}
});
}
Edit
call it:
for (i = 0; i < nameList.size(); i++) {
String couponName = nameList.get(i);
storageHandler.downloadImagesFromFireStorage(imgName, new BitmapCallBackInterface() {
#Override
public void onCallBack(Bitmap bitmap) {
insertImg(couponName, bitmap); // store image into SQLite
}
});
}
getDataFromSQLite();
// and then display Bitmaps to the a list of ImageView
Edit
someone had taught me that the thread will continue after receive callback, so i write a for loop to download all the images from firebase-storage, store them all into SQlite database, then retrieve them out and display to ImageView. But it seems like the thread continues before the image download complete? how can i fix it? Maybe i am misunderstanding what i had learned?
what i exectly want to do is, download several images, after all downloading complete, refresh the listview
First, I hope you are using a CursorAdapter for your listview, and not pulling data from sqlite, to an arraylist, to an ArrayAdapter, etc.
Secondly, you ideally shouldn't be storing listview images as part of the database. You can store firebase links, and then in the adapter, you will request the image content (using an image loading library such as Glide, see below)
In any case, what you need to do is notify the adapter dataSetChanged for every call to onCallBack, after your image is inserted, then the list will update after the image is in sqlite.
If you want to wait for all images to be inserted before you notify the adapter, you can check the inserted image position against nameList.size()
You might also want to consider just using the OnSuccessListener<byte[]> interface for storing bytes into sqlite as a BLOB, not necessarily a Bitmap object. Under that scenario, you don't need that extra interface
However, the encouraged pattern on the Android documentation for image loading in general is to use Glide
Getting Image from Firebase Storage using Glide
I pass the image from drawable reference like follows
Bitmap bitmap = mImageGenerator.generateDateImage(calendar,
R.drawable.data);
it throws
java.lang.IllegalStateException: Immutable bitmap passed to Canvas
constructor
I use same exact statement in other activity and it works fine.
Problem :
mImageGenerator.generateDateImage is locked class from caldroid
(https://github.com/roomorama/Caldroid)
so cant change anything in it.
I just noticed that this occurs on NOUGAT
How do I solve this issue?
So the solution is to add
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inMutable = true;<<<<<<
in custom created class ownImageGenerator
I am looking at a code sample that explains the proxy pattern. Here is the code:
/**
* Proxy
*/
public class ImageProxy implements Image {
/**
* Private Proxy data
*/
private String imageFilePath;
/**
* Reference to RealSubject
*/
private Image proxifiedImage;
public ImageProxy(String imageFilePath) {
this.imageFilePath= imageFilePath;
}
#Override
public void showImage() {
// create the Image Object only when the image is required to be shown
proxifiedImage = new HighResolutionImage(imageFilePath);
// now call showImage on realSubject
proxifiedImage.showImage();
}
}
/**
* RealSubject
*/
public class HighResolutionImage implements Image {
public HighResolutionImage(String imageFilePath) {
loadImage(imageFilePath);
}
private void loadImage(String imageFilePath) {
// load Image from disk into memory
// this is heavy and costly operation
}
#Override
public void showImage() {
// Actual Image rendering logic
}
}
/**
* Image Viewer program
*/
public class ImageViewer {
public static void main(String[] args) {
// assuming that the user selects a folder that has 3 images
//create the 3 images
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
// assume that the user clicks on Image one item in a list
// this would cause the program to call showImage() for that image only
// note that in this case only image one was loaded into memory
highResolutionImage1.showImage();
// consider using the high resolution image object directly
Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
Image highResolutionImageBoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");
// assume that the user selects image two item from images list
highResolutionImageNoProxy2.showImage();
// note that in this case all images have been loaded into memory
// and not all have been actually displayed
// this is a waste of memory resources
}
}
Assume proxy pattern is implemented correctly, and this is the main method of the program. Here is what i wonder: The comments in the code say that when we use proxy image objects, if we load a picture into memory, just that image is loaded. But if we do not use proxy and directly create real images, when we load an instance of this class, we load all instances of the class into memory. I do not understand why this is the case. Yes, the whole point of proxy pattern is to do this, but i do not understand why all 3 of the highResolutionImageNoProxy objects are loaded into memory when we call highResolutionImageNoProxy2.showImage(); . Can anyonbody explain it?
Thanks
Edit: I think i figured out why. Because the ImageProxy class calls the constructor of HighResolutionImage class only when it tries to do an operation on the object, but if we create a HighResolutionImage directly, then since its constructor creates the object, all of them are loaded into memory.
The code assumes that when you create an instance of HighResolutionImage, the image is loaded to the memory, even if showImage() isn't called.
The proxy will assure that the image is loaded to the memory only when showImage() is called.
//load veryHighResPhoto1 to memory
Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
//load veryHighResPhoto2 to memory
Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
//load veryHighResPhoto3 to memory
Image highResolutionImageBoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");
//load just the proxys (image not loaded yet)
Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");
//trigger the load of the image into memory
highResolutionImage1.showImage();
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
OutOfMemory Exception when handling images
I have a Bitmap creation within a AsyncTask, which goes like this:
private WeakReference<Bitmap> myBitmap;
private WeakReference<Bitmap> endResultBitmap;
private ImageView imv;
...
private class SendBitmap extends AsyncTask<Integer, Void, Bitmap> {
public SendBitmap(Bitmap bitmap) {
myBitmap = new WeakReference<Bitmap>(bitmap);
}
#Override
protected Bitmap doInBackground(Integer... params) {
Bitmap bm = null;
bm = getBitmapFromNet(params[0]);
return bm;
}
And then I want to create Bitmap on which the received Bitmap would appear twice (one next to another)
protected void onPostExecute(Bitmap result) {
endResultBitmap = new WeakReference<Bitmap>(Bitmap.createBitmap(result.getWidth() * 2, result.getHeight(), result.getConf()));
Canvas canvas = new Canvas(endResultBitmap.get());
canvas.drawBitmap(result, 0, 0, null);
canvas.drawBitmap(result, result.getWidth(), 0, null);
imv.setImageBitmap(endResultBitmap);
}
then I have my onCancelled() method:
#Override
protected void onCancelled(Bitmap result) {
if(endResultBitmap!=null) {
endResultBitmap.recycle();
endResultBitmap = null;
}
}
The thing is that if I execute this AsyncTask couple of times, the heap grows as mad.
I execute the AsyncTask when a button is pressed, but at first I do:
public void onClicked(View v) {
if(asyncTaskInstance != null)
asyncTaskInstance.cancel();
asynctaskInstance.execute(2);
}
But again, the Heap grows as mad and at some point it will crash with OutOfMemoryError.
Any idea? Do I have something wrong in my Design of the task?
Android has some memory limits for apps (16 MB if i remember correctly), and that image is too big in an uncompressed format. Theres some interesting discussion in this question.
To solve it, there are afaik only two ways:
1. Reduce the size of the image to consume less memory
2. Load the image in native code using the NDK.
To 1.: I don't know what exactly you are trying to do and can't tell if thats really a viable option. If it is, you may want download the image file from the net and open it with the BitmapFactory class. There are some static functions that take an BitmapFactory.Options object. Use inSampleSize from this Options object to reduce the image size by a certain factor at loading time (inSampleSize should be a power of two by the way).
To 2.: The memory limits that I mentioned above don't apply to native code. So you may be able to load the image and display in a native way. I don't have any experience with that, I just know that it's possible, but googling around should turn up a few results.
I have a getViewBitmap() method that creates a Bitmap with a 3rd party library (which is proprietary) and goes like this:
public Bitmap getViewBitmap(int index) {
Bitmap retBitmap = null;
int width = 400;
int height = 200;
try {
retBitmap = lib.createBitmap(width, height, index);
} catch(BMException e) {
e.printStacktrace();
}
return retBitmap;
}
This method is used for creating two page view bitmap in another method:
public Bitmap getTwoPageBitmap(int firstPageIndex, intSecondPageIndex) {
Bitmap first = getViewBitmap(firstPageIndex);
Bitmap second = getViewBitmap(secondPageIndex);
Bitmap retBitmap = Bitmap.create(800, 400, first.getConfig());
Canvas helperCanvas = new Canvas(splitViewBm);
helperCanvas.drawBitmap(leftPageBitmap, 0, 0, null);
helperCanvas.drawBitmap(rightPageBitmap, leftPageBitmap.getWidth(), 0, null);
return retBitmap;
}
And then finally in initiated method, I have this:
public View createView() {
MyView v = new MyView();
if(pagePortratit) {
v.setPageView(getViewBitmap(0));
} else {
// if page is landscape
v.setPageView(getTwoPageBitmap(0, 1));
}
return v;
}
Now - I wanna make the getViewBitmap(int) method Asynchronous. Since the "lib.createBitmap(int, int, int)" is pretty slow and it blocks the UI, I want the creation of the bitmap (getViewBitmap(int)) to be in another thread, with possibility to interrupt it's work.
What is the correct design for such design so that the method that is actually heavy goes async?
You likely want to subclass AsyncTask (read here) and put your getBitmapView code in the doInBackground() method (#Override). When it's done, have the onPostExecute() method update the View/UI. The logic for determining landscape or portrait will want to be outside the AsyncTask and you'll just want to farm out to the task (using .execute()) which ever view is needed.
That might be one approach.
I think the best thing that you can do is to use AysncTask which allows u to update the UI Directly without special handling for UI Thread update.