TaskPersister.java revision 648f69b95ce7fc95f551f6e08a2408d6e57dbab9
14199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa/* 24199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Copyright (C) 2014 The Android Open Source Project 34199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 44199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 54199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * you may not use this file except in compliance with the License. 64199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * You may obtain a copy of the License at 74199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 84199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 94199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * 104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * See the License for the specific language governing permissions and 144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * limitations under the License. 154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa */ 16677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa 174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapackage com.android.server.am; 187e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa 197e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.graphics.Bitmap; 207e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.graphics.BitmapFactory; 217e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.os.Debug; 227e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.os.SystemClock; 237e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.util.ArraySet; 247e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.util.AtomicFile; 257e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.util.Slog; 267e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawaimport android.util.Xml; 272c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport com.android.internal.util.FastXmlSerializer; 282c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport com.android.internal.util.XmlUtils; 292c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport org.xmlpull.v1.XmlPullParser; 302c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport org.xmlpull.v1.XmlPullParserException; 312c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport org.xmlpull.v1.XmlSerializer; 322c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng 332c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Chengimport java.io.BufferedReader; 34a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawaimport java.io.File; 35a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawaimport java.io.FileOutputStream; 364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.FileReader; 374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.IOException; 384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.io.StringWriter; 394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.ArrayList; 404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Arrays; 414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawaimport java.util.Comparator; 424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawapublic class TaskPersister { 444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static final String TAG = "TaskPersister"; 454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa static final boolean DEBUG = false; 464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 474199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** When not flushing don't write out files faster than this */ 484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final long INTER_WRITE_DELAY_MS = 500; 494199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** When not flushing delay this long before writing the first file out. This gives the next 514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * task being launched a chance to load its resources without this occupying IO bandwidth. */ 524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final long PRE_TASK_DELAY_MS = 3000; 534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 5456174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa private static final String RECENTS_FILENAME = "_task"; 554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static final String TASKS_DIRNAME = "recent_tasks"; 567e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa private static final String TASK_EXTENSION = ".xml"; 577e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa private static final String IMAGES_DIRNAME = "recent_images"; 587e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa static final String IMAGE_EXTENSION = ".png"; 597e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa 607e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa private static final String TAG_TASK = "task"; 617e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa 627e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa static File sImagesDir; 63677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa static File sTasksDir; 647e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa 654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private final ActivityManagerService mService; 664199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private final ActivityStackSupervisor mStackSupervisor; 674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa /** Value determines write delay mode as follows: 694199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * < 0 We are Flushing. No delays between writes until the image queue is drained and all 704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * tasks needing persisting are written to disk. There is no delay between writes. 714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS. 724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be 734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa * delayed by #INTER_WRITE_DELAY_MS. */ 744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private long mNextWriteTime = 0; 754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private final LazyTaskWriterThread mLazyTaskWriterThread; 77a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa 784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static class WriteQueueItem {} 794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static class TaskWriteQueueItem extends WriteQueueItem { 804199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final TaskRecord mTask; 814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa TaskWriteQueueItem(TaskRecord task) { 824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mTask = task; 834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static class ImageWriteQueueItem extends WriteQueueItem { 864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String mFilename; 874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Bitmap mImage; 884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ImageWriteQueueItem(String filename, Bitmap image) { 894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mFilename = filename; 904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mImage = image; 914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 934199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 942c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>(); 952c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng 964199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) { 97a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa sTasksDir = new File(systemDir, TASKS_DIRNAME); 98a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (!sTasksDir.exists()) { 994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir); 1004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!sTasksDir.mkdir()) { 1014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Slog.e(TAG, "Failure creating tasks directory " + sTasksDir); 1024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa sImagesDir = new File(systemDir, IMAGES_DIRNAME); 1064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!sImagesDir.exists()) { 1074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir); 108be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (!sImagesDir.mkdir()) { 1094199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Slog.e(TAG, "Failure creating images directory " + sImagesDir); 1104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1124199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mStackSupervisor = stackSupervisor; 1144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mService = stackSupervisor.mService; 1154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread"); 1174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1194199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa void startPersisting() { 1204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mLazyTaskWriterThread.start(); 1214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 123be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa void wakeup(TaskRecord task, boolean flush) { 1244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa synchronized (this) { 125a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (task != null) { 1264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int queueNdx; 1274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) { 1284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final WriteQueueItem item = mWriteQueue.get(queueNdx); 1294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (item instanceof TaskWriteQueueItem && 1304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ((TaskWriteQueueItem) item).mTask == task) { 1314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 1324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (queueNdx < 0) { 1354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mWriteQueue.add(new TaskWriteQueueItem(task)); 1364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 137677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa } else { 138677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa // Dummy. 1394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mWriteQueue.add(new WriteQueueItem()); 140be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 1414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (flush) { 1424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mNextWriteTime = -1; 1434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (mNextWriteTime == 0) { 1444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS; 1454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1464199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime=" 147be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa + mNextWriteTime + " Callers=" + Debug.getCallers(4)); 1484199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa notifyAll(); 149be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 1504199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 1514199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1524199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa void saveImage(Bitmap image, String filename) { 1534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa synchronized (this) { 1544199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int queueNdx; 1554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) { 156be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa final WriteQueueItem item = mWriteQueue.get(queueNdx); 1574199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (item instanceof ImageWriteQueueItem) { 1584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item; 159be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (imageWriteQueueItem.mFilename.equals(filename)) { 1604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // replace the Bitmap with the new one. 1614199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa imageWriteQueueItem.mImage = image; 1624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa break; 1634199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 164a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 1654199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 166be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (queueNdx < 0) { 1674199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mWriteQueue.add(new ImageWriteQueueItem(filename, image)); 1684199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 169a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (mNextWriteTime == 0) { 1704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS; 171a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 172a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" + 173677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa SystemClock.uptimeMillis() + " mNextWriteTime=" + 174a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa mNextWriteTime + " Callers=" + Debug.getCallers(4)); 175a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa notifyAll(); 176be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 177be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 178be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa 179be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa Bitmap getTaskDescriptionIcon(String filename) { 180be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa // See if it is in the write queue 181be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa final Bitmap icon = getImageFromWriteQueue(filename); 182be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (icon != null) { 183be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return icon; 184be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 185be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa return restoreImage(filename); 186be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 1874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 1884199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Bitmap getImageFromWriteQueue(String filename) { 1894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa synchronized (this) { 1904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) { 1914199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final WriteQueueItem item = mWriteQueue.get(queueNdx); 1924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (item instanceof ImageWriteQueueItem) { 193be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item; 1944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (imageWriteQueueItem.mFilename.equals(filename)) { 1953d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa return imageWriteQueueItem.mImage; 1963d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 1973d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 1983d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 1993d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa return null; 2003d77102a83d0e412046ca0ff9dfdef1a44050ca3Daisuke Miyakawa } 2014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2024199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException { 2044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task); 2054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final XmlSerializer xmlSerializer = new FastXmlSerializer(); 2064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa StringWriter stringWriter = new StringWriter(); 2074199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa xmlSerializer.setOutput(stringWriter); 2084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 209be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (DEBUG) xmlSerializer.setFeature( 2104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa "http://xmlpull.org/v1/doc/features.html#indent-output", true); 2114199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 212a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa // save task 2134199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa xmlSerializer.startDocument(null, true); 2144199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2154199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa xmlSerializer.startTag(null, TAG_TASK); 2164199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa task.saveToXml(xmlSerializer); 2174199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa xmlSerializer.endTag(null, TAG_TASK); 2184199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 219be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa xmlSerializer.endDocument(); 2204199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa xmlSerializer.flush(); 2214199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2224199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return stringWriter; 2234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 225a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa private String fileToString(File file) { 2264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String newline = System.lineSeparator(); 2274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 2282c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng BufferedReader reader = new BufferedReader(new FileReader(file)); 2292c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng StringBuffer sb = new StringBuffer((int) file.length() * 2); 2302c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng String line; 2312c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng while ((line = reader.readLine()) != null) { 232a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa sb.append(line + newline); 233677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa } 234a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa reader.close(); 235a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa return sb.toString(); 236a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } catch (IOException ioe) { 237a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa Slog.e(TAG, "Couldn't read file " + file.getName()); 238a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa return null; 2394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 2414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private TaskRecord taskIdToTask(int taskId, ArrayList<TaskRecord> tasks) { 243be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (taskId < 0) { 2449ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa return null; 2459ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa } 2469ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 2479ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa final TaskRecord task = tasks.get(taskNdx); 2489ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa if (task.taskId == taskId) { 2499ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa return task; 2509ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa } 2519ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa } 2529ec6c05a1dbb17862a44f96e91975dfc01cebf6eDaisuke Miyakawa Slog.e(TAG, "Restore affiliation error looking for taskId=" + taskId); 2532c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng return null; 2542c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng } 2552c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng 2562c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng ArrayList<TaskRecord> restoreTasksLocked() { 257a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>(); 2582c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>(); 2592c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng 2602c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng File[] recentFiles = sTasksDir.listFiles(); 2612c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng if (recentFiles == null) { 2622c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng Slog.e(TAG, "Unable to list files from " + sTasksDir); 2632c9cf383b1c956c7185e97c2417ebd85b48fc0acChiao Cheng return tasks; 264a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 265a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa 266a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) { 267a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa File taskFile = recentFiles[taskNdx]; 268677ef21613a9d35053ec098444832ce4125a847eDaisuke Miyakawa if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName()); 269a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa BufferedReader reader = null; 270a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa boolean deleteFile = false; 2714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa try { 272a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa reader = new BufferedReader(new FileReader(taskFile)); 2734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final XmlPullParser in = Xml.newPullParser(); 274be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa in.setInput(reader); 2754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 2764199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa int event; 2774199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && 2784199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa event != XmlPullParser.END_TAG) { 2794199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final String name = in.getName(); 280be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa if (event == XmlPullParser.START_TAG) { 2814199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name); 2824199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (TAG_TASK.equals(name)) { 2834199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final TaskRecord task = 2844199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa TaskRecord.restoreFromXml(in, mStackSupervisor); 2854199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + 2864199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa task); 2874199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (task != null) { 288a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa task.isPersistable = true; 2894199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // XXX Don't add to write queue... there is no reason to write 2904199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // out the stuff we just read, if we don't write it we will 291a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa // read the same thing again. 2924199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa //mWriteQueue.add(new TaskWriteQueueItem(task)); 293a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa tasks.add(task); 2944199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final int taskId = task.taskId; 295a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa recoveredTaskIds.add(taskId); 296a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa mStackSupervisor.setNextTaskId(taskId); 2974199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 298a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " + 2994199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa fileToString(taskFile)); 3004199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3014199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else { 302a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event + 3034199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa " name=" + name); 3044199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3054199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3064199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa XmlUtils.skipCurrentTag(in); 307a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 3084199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (Exception e) { 309be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e); 3104199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Slog.e(TAG, "Failing file: " + fileToString(taskFile)); 311a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa deleteFile = true; 312a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } finally { 313a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (reader != null) { 314a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa try { 315a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa reader.close(); 316a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } catch (IOException e) { 317a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 318a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 319a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (!DEBUG && deleteFile) { 320a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa if (true || DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName()); 321a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa taskFile.delete(); 322a76f41e328f31c2e9e9006160d8f65fe651eeb6aDaisuke Miyakawa } 3234199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3244199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3254199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3264199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (!DEBUG) { 3274199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa removeObsoleteFiles(recoveredTaskIds); 3284199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3294199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3304199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa // Fixup task affiliation from taskIds 3314199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 3324199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final TaskRecord task = tasks.get(taskNdx); 3334199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks)); 3344199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks)); 3354199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 3364199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3374199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa TaskRecord[] tasksArray = new TaskRecord[tasks.size()]; 3384199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa tasks.toArray(tasksArray); 3394199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Arrays.sort(tasksArray, new Comparator<TaskRecord>() { 3404199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa @Override 3414199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa public int compare(TaskRecord lhs, TaskRecord rhs) { 3424199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved; 3434199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (diff < 0) { 3444199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return -1; 3454199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } else if (diff > 0) { 3467e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa return +1; 3477e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa } else { 3487e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa return 0; 3497e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa } 3507e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa } 3517e4e86eb5ad2c8a68ca7005ef4dee64a82ce0198Daisuke Miyakawa }); 352be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa 3534199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa return new ArrayList<TaskRecord>(Arrays.asList(tasksArray)); 354be378d5b188f51cf717e5309e3c39180e85833a8Daisuke Miyakawa } 3554199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa 3564199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) { 35756174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds + 3584199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa " files=" + files); 35956174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa if (files == null) { 3604199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Slog.e(TAG, "File error accessing recents directory (too many files open?)."); 36156174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa return; 3624199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } 36356174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) { 36456174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa File file = files[fileNdx]; 36556174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa String filename = file.getName(); 36656174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa final int taskIdEnd = filename.indexOf('_'); 36756174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa if (taskIdEnd > 0) { 36856174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa final int taskId; 36956174dfd0654acbe828e4db38537ec5a3a04d466Daisuke Miyakawa try { 3704199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa taskId = Integer.valueOf(filename.substring(0, taskIdEnd)); 3714199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId); 3724199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa } catch (Exception e) { 3734199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName()); 3744199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa file.delete(); 3754199c54c527330ac01699b176e7bca186a3aa3a4Daisuke Miyakawa continue; 376 } 377 if (!persistentTaskIds.contains(taskId)) { 378 if (true || DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + 379 file.getName()); 380 file.delete(); 381 } 382 } 383 } 384 } 385 386 private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) { 387 removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles()); 388 removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles()); 389 } 390 391 static Bitmap restoreImage(String filename) { 392 if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename); 393 return BitmapFactory.decodeFile(sImagesDir + File.separator + filename); 394 } 395 396 private class LazyTaskWriterThread extends Thread { 397 398 LazyTaskWriterThread(String name) { 399 super(name); 400 } 401 402 @Override 403 public void run() { 404 ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>(); 405 while (true) { 406 // We can't lock mService while holding TaskPersister.this, but we don't want to 407 // call removeObsoleteFiles every time through the loop, only the last time before 408 // going to sleep. The risk is that we call removeObsoleteFiles() successively. 409 final boolean probablyDone; 410 synchronized (TaskPersister.this) { 411 probablyDone = mWriteQueue.isEmpty(); 412 } 413 if (probablyDone) { 414 if (DEBUG) Slog.d(TAG, "Looking for obsolete files."); 415 persistentTaskIds.clear(); 416 synchronized (mService) { 417 final ArrayList<TaskRecord> tasks = mService.mRecentTasks; 418 if (DEBUG) Slog.d(TAG, "mRecents=" + tasks); 419 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { 420 final TaskRecord task = tasks.get(taskNdx); 421 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" + 422 task.isPersistable); 423 if (task.isPersistable && !task.stack.isHomeStack()) { 424 if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); 425 persistentTaskIds.add(task.taskId); 426 } else { 427 if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task); 428 } 429 } 430 } 431 removeObsoleteFiles(persistentTaskIds); 432 } 433 434 // If mNextWriteTime, then don't delay between each call to saveToXml(). 435 final WriteQueueItem item; 436 synchronized (TaskPersister.this) { 437 if (mNextWriteTime >= 0) { 438 // The next write we don't have to wait so long. 439 mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS; 440 if (DEBUG) Slog.d(TAG, "Next write time may be in " + 441 INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")"); 442 } 443 444 while (mWriteQueue.isEmpty()) { 445 mNextWriteTime = 0; // idle. 446 try { 447 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely."); 448 TaskPersister.this.wait(); 449 } catch (InterruptedException e) { 450 } 451 // Invariant: mNextWriteTime is either -1 or PRE_WRITE_DELAY_MS from now. 452 } 453 item = mWriteQueue.remove(0); 454 455 long now = SystemClock.uptimeMillis(); 456 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + 457 mNextWriteTime); 458 while (now < mNextWriteTime) { 459 try { 460 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " + 461 (mNextWriteTime - now)); 462 TaskPersister.this.wait(mNextWriteTime - now); 463 } catch (InterruptedException e) { 464 } 465 now = SystemClock.uptimeMillis(); 466 } 467 468 // Got something to do. 469 } 470 471 if (item instanceof ImageWriteQueueItem) { 472 ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item; 473 final String filename = imageWriteQueueItem.mFilename; 474 final Bitmap bitmap = imageWriteQueueItem.mImage; 475 if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename); 476 FileOutputStream imageFile = null; 477 try { 478 imageFile = new FileOutputStream(new File(sImagesDir, filename)); 479 bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile); 480 } catch (Exception e) { 481 Slog.e(TAG, "saveImage: unable to save " + filename, e); 482 } finally { 483 if (imageFile != null) { 484 try { 485 imageFile.close(); 486 } catch (IOException e) { 487 } 488 } 489 } 490 } else if (item instanceof TaskWriteQueueItem) { 491 // Write out one task. 492 StringWriter stringWriter = null; 493 TaskRecord task = ((TaskWriteQueueItem) item).mTask; 494 if (DEBUG) Slog.d(TAG, "Writing task=" + task); 495 synchronized (mService) { 496 if (mService.mRecentTasks.contains(task)) { 497 // Still there. 498 try { 499 if (DEBUG) Slog.d(TAG, "Saving task=" + task); 500 stringWriter = saveToXml(task); 501 } catch (IOException e) { 502 } catch (XmlPullParserException e) { 503 } 504 } 505 } 506 if (stringWriter != null) { 507 // Write out xml file while not holding mService lock. 508 FileOutputStream file = null; 509 AtomicFile atomicFile = null; 510 try { 511 atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf( 512 task.taskId) + RECENTS_FILENAME + TASK_EXTENSION)); 513 file = atomicFile.startWrite(); 514 file.write(stringWriter.toString().getBytes()); 515 file.write('\n'); 516 atomicFile.finishWrite(file); 517 } catch (IOException e) { 518 if (file != null) { 519 atomicFile.failWrite(file); 520 } 521 Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " + 522 e); 523 } 524 } 525 } 526 } 527 } 528 } 529} 530