Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2014 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.server.am;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.AppGlobals;
     21 import android.content.ComponentName;
     22 import android.content.pm.IPackageManager;
     23 import android.graphics.Bitmap;
     24 import android.graphics.BitmapFactory;
     25 import android.os.Debug;
     26 import android.os.RemoteException;
     27 import android.os.SystemClock;
     28 import android.os.UserHandle;
     29 import android.text.format.DateUtils;
     30 import android.util.ArrayMap;
     31 import android.util.ArraySet;
     32 import android.util.AtomicFile;
     33 import android.util.Slog;
     34 import android.util.SparseArray;
     35 import android.util.Xml;
     36 import android.os.Process;
     37 
     38 import com.android.internal.util.FastXmlSerializer;
     39 import com.android.internal.util.XmlUtils;
     40 
     41 import org.xmlpull.v1.XmlPullParser;
     42 import org.xmlpull.v1.XmlPullParserException;
     43 import org.xmlpull.v1.XmlSerializer;
     44 
     45 import java.io.BufferedReader;
     46 import java.io.File;
     47 import java.io.FileOutputStream;
     48 import java.io.FileReader;
     49 import java.io.IOException;
     50 import java.io.StringWriter;
     51 import java.util.ArrayList;
     52 import java.util.Arrays;
     53 import java.util.Collections;
     54 import java.util.Comparator;
     55 import java.util.List;
     56 
     57 import libcore.io.IoUtils;
     58 
     59 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
     60 
     61 public class TaskPersister {
     62     static final String TAG = "TaskPersister";
     63     static final boolean DEBUG = false;
     64 
     65     /** When not flushing don't write out files faster than this */
     66     private static final long INTER_WRITE_DELAY_MS = 500;
     67 
     68     /** When not flushing delay this long before writing the first file out. This gives the next
     69      * task being launched a chance to load its resources without this occupying IO bandwidth. */
     70     private static final long PRE_TASK_DELAY_MS = 3000;
     71 
     72     /** The maximum number of entries to keep in the queue before draining it automatically. */
     73     private static final int MAX_WRITE_QUEUE_LENGTH = 6;
     74 
     75     /** Special value for mWriteTime to mean don't wait, just write */
     76     private static final long FLUSH_QUEUE = -1;
     77 
     78     private static final String RECENTS_FILENAME = "_task";
     79     private static final String TASKS_DIRNAME = "recent_tasks";
     80     private static final String TASK_EXTENSION = ".xml";
     81     private static final String IMAGES_DIRNAME = "recent_images";
     82     static final String IMAGE_EXTENSION = ".png";
     83 
     84     private static final String TAG_TASK = "task";
     85 
     86     static File sImagesDir;
     87     static File sTasksDir;
     88 
     89     private final ActivityManagerService mService;
     90     private final ActivityStackSupervisor mStackSupervisor;
     91     private final RecentTasks mRecentTasks;
     92 
     93     /** Value determines write delay mode as follows:
     94      *    < 0 We are Flushing. No delays between writes until the image queue is drained and all
     95      * tasks needing persisting are written to disk. There is no delay between writes.
     96      *    == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS.
     97      *    > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be
     98      * delayed by #INTER_WRITE_DELAY_MS. */
     99     private long mNextWriteTime = 0;
    100 
    101     private final LazyTaskWriterThread mLazyTaskWriterThread;
    102 
    103     private static class WriteQueueItem {}
    104     private static class TaskWriteQueueItem extends WriteQueueItem {
    105         final TaskRecord mTask;
    106         TaskWriteQueueItem(TaskRecord task) {
    107             mTask = task;
    108         }
    109     }
    110     private static class ImageWriteQueueItem extends WriteQueueItem {
    111         final String mFilename;
    112         Bitmap mImage;
    113         ImageWriteQueueItem(String filename, Bitmap image) {
    114             mFilename = filename;
    115             mImage = image;
    116         }
    117     }
    118 
    119     ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
    120 
    121     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
    122             RecentTasks recentTasks) {
    123         sTasksDir = new File(systemDir, TASKS_DIRNAME);
    124         if (!sTasksDir.exists()) {
    125             if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
    126             if (!sTasksDir.mkdir()) {
    127                 Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
    128             }
    129         }
    130 
    131         sImagesDir = new File(systemDir, IMAGES_DIRNAME);
    132         if (!sImagesDir.exists()) {
    133             if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
    134             if (!sImagesDir.mkdir()) {
    135                 Slog.e(TAG, "Failure creating images directory " + sImagesDir);
    136             }
    137         }
    138 
    139         mStackSupervisor = stackSupervisor;
    140         mService = stackSupervisor.mService;
    141         mRecentTasks = recentTasks;
    142         mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
    143     }
    144 
    145     void startPersisting() {
    146         if (!mLazyTaskWriterThread.isAlive()) {
    147             mLazyTaskWriterThread.start();
    148         }
    149     }
    150 
    151     private void removeThumbnails(TaskRecord task) {
    152         final String taskString = Integer.toString(task.taskId);
    153         for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
    154             final WriteQueueItem item = mWriteQueue.get(queueNdx);
    155             if (item instanceof ImageWriteQueueItem &&
    156                     ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
    157                 if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
    158                         " from write queue");
    159                 mWriteQueue.remove(queueNdx);
    160             }
    161         }
    162     }
    163 
    164     private void yieldIfQueueTooDeep() {
    165         boolean stall = false;
    166         synchronized (this) {
    167             if (mNextWriteTime == FLUSH_QUEUE) {
    168                 stall = true;
    169             }
    170         }
    171         if (stall) {
    172             Thread.yield();
    173         }
    174     }
    175 
    176     void wakeup(TaskRecord task, boolean flush) {
    177         synchronized (this) {
    178             if (task != null) {
    179                 int queueNdx;
    180                 for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
    181                     final WriteQueueItem item = mWriteQueue.get(queueNdx);
    182                     if (item instanceof TaskWriteQueueItem &&
    183                             ((TaskWriteQueueItem) item).mTask == task) {
    184                         if (!task.inRecents) {
    185                             // This task is being removed.
    186                             removeThumbnails(task);
    187                         }
    188                         break;
    189                     }
    190                 }
    191                 if (queueNdx < 0 && task.isPersistable) {
    192                     mWriteQueue.add(new TaskWriteQueueItem(task));
    193                 }
    194             } else {
    195                 // Dummy.
    196                 mWriteQueue.add(new WriteQueueItem());
    197             }
    198             if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
    199                 mNextWriteTime = FLUSH_QUEUE;
    200             } else if (mNextWriteTime == 0) {
    201                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
    202             }
    203             if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
    204                     + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
    205                     + " Callers=" + Debug.getCallers(4));
    206             notifyAll();
    207         }
    208 
    209         yieldIfQueueTooDeep();
    210     }
    211 
    212     void flush() {
    213         synchronized (this) {
    214             mNextWriteTime = FLUSH_QUEUE;
    215             notifyAll();
    216             do {
    217                 try {
    218                     wait();
    219                 } catch (InterruptedException e) {
    220                 }
    221             } while (mNextWriteTime == FLUSH_QUEUE);
    222         }
    223     }
    224 
    225     void saveImage(Bitmap image, String filename) {
    226         synchronized (this) {
    227             int queueNdx;
    228             for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
    229                 final WriteQueueItem item = mWriteQueue.get(queueNdx);
    230                 if (item instanceof ImageWriteQueueItem) {
    231                     ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
    232                     if (imageWriteQueueItem.mFilename.equals(filename)) {
    233                         // replace the Bitmap with the new one.
    234                         imageWriteQueueItem.mImage = image;
    235                         break;
    236                     }
    237                 }
    238             }
    239             if (queueNdx < 0) {
    240                 mWriteQueue.add(new ImageWriteQueueItem(filename, image));
    241             }
    242             if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
    243                 mNextWriteTime = FLUSH_QUEUE;
    244             } else if (mNextWriteTime == 0) {
    245                 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
    246             }
    247             if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
    248                     SystemClock.uptimeMillis() + " mNextWriteTime=" +
    249                     mNextWriteTime + " Callers=" + Debug.getCallers(4));
    250             notifyAll();
    251         }
    252 
    253         yieldIfQueueTooDeep();
    254     }
    255 
    256     Bitmap getTaskDescriptionIcon(String filename) {
    257         // See if it is in the write queue
    258         final Bitmap icon = getImageFromWriteQueue(filename);
    259         if (icon != null) {
    260             return icon;
    261         }
    262         return restoreImage(filename);
    263     }
    264 
    265     Bitmap getImageFromWriteQueue(String filename) {
    266         synchronized (this) {
    267             for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
    268                 final WriteQueueItem item = mWriteQueue.get(queueNdx);
    269                 if (item instanceof ImageWriteQueueItem) {
    270                     ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
    271                     if (imageWriteQueueItem.mFilename.equals(filename)) {
    272                         return imageWriteQueueItem.mImage;
    273                     }
    274                 }
    275             }
    276             return null;
    277         }
    278     }
    279 
    280     private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
    281         if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
    282         final XmlSerializer xmlSerializer = new FastXmlSerializer();
    283         StringWriter stringWriter = new StringWriter();
    284         xmlSerializer.setOutput(stringWriter);
    285 
    286         if (DEBUG) xmlSerializer.setFeature(
    287                     "http://xmlpull.org/v1/doc/features.html#indent-output", true);
    288 
    289         // save task
    290         xmlSerializer.startDocument(null, true);
    291 
    292         xmlSerializer.startTag(null, TAG_TASK);
    293         task.saveToXml(xmlSerializer);
    294         xmlSerializer.endTag(null, TAG_TASK);
    295 
    296         xmlSerializer.endDocument();
    297         xmlSerializer.flush();
    298 
    299         return stringWriter;
    300     }
    301 
    302     private String fileToString(File file) {
    303         final String newline = System.lineSeparator();
    304         try {
    305             BufferedReader reader = new BufferedReader(new FileReader(file));
    306             StringBuffer sb = new StringBuffer((int) file.length() * 2);
    307             String line;
    308             while ((line = reader.readLine()) != null) {
    309                 sb.append(line + newline);
    310             }
    311             reader.close();
    312             return sb.toString();
    313         } catch (IOException ioe) {
    314             Slog.e(TAG, "Couldn't read file " + file.getName());
    315             return null;
    316         }
    317     }
    318 
    319     private TaskRecord taskIdToTask(int taskId, ArrayList<TaskRecord> tasks) {
    320         if (taskId < 0) {
    321             return null;
    322         }
    323         for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
    324             final TaskRecord task = tasks.get(taskNdx);
    325             if (task.taskId == taskId) {
    326                 return task;
    327             }
    328         }
    329         Slog.e(TAG, "Restore affiliation error looking for taskId=" + taskId);
    330         return null;
    331     }
    332 
    333     ArrayList<TaskRecord> restoreTasksLocked() {
    334         final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
    335         ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
    336 
    337         File[] recentFiles = sTasksDir.listFiles();
    338         if (recentFiles == null) {
    339             Slog.e(TAG, "Unable to list files from " + sTasksDir);
    340             return tasks;
    341         }
    342 
    343         for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
    344             File taskFile = recentFiles[taskNdx];
    345             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
    346             BufferedReader reader = null;
    347             boolean deleteFile = false;
    348             try {
    349                 reader = new BufferedReader(new FileReader(taskFile));
    350                 final XmlPullParser in = Xml.newPullParser();
    351                 in.setInput(reader);
    352 
    353                 int event;
    354                 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
    355                         event != XmlPullParser.END_TAG) {
    356                     final String name = in.getName();
    357                     if (event == XmlPullParser.START_TAG) {
    358                         if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
    359                         if (TAG_TASK.equals(name)) {
    360                             final TaskRecord task =
    361                                     TaskRecord.restoreFromXml(in, mStackSupervisor);
    362                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
    363                                     task);
    364                             if (task != null) {
    365                                 task.isPersistable = true;
    366                                 // XXX Don't add to write queue... there is no reason to write
    367                                 // out the stuff we just read, if we don't write it we will
    368                                 // read the same thing again.
    369                                 //mWriteQueue.add(new TaskWriteQueueItem(task));
    370                                 tasks.add(task);
    371                                 final int taskId = task.taskId;
    372                                 recoveredTaskIds.add(taskId);
    373                                 mStackSupervisor.setNextTaskId(taskId);
    374                             } else {
    375                                 Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
    376                                         fileToString(taskFile));
    377                             }
    378                         } else {
    379                             Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
    380                                     " name=" + name);
    381                         }
    382                     }
    383                     XmlUtils.skipCurrentTag(in);
    384                 }
    385             } catch (Exception e) {
    386                 Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
    387                 Slog.e(TAG, "Failing file: " + fileToString(taskFile));
    388                 deleteFile = true;
    389             } finally {
    390                 IoUtils.closeQuietly(reader);
    391                 if (deleteFile) {
    392                     if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
    393                     taskFile.delete();
    394                 }
    395             }
    396         }
    397 
    398         if (!DEBUG) {
    399             removeObsoleteFiles(recoveredTaskIds);
    400         }
    401 
    402         // Fixup task affiliation from taskIds
    403         for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
    404             final TaskRecord task = tasks.get(taskNdx);
    405             task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
    406             task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks));
    407         }
    408 
    409         TaskRecord[] tasksArray = new TaskRecord[tasks.size()];
    410         tasks.toArray(tasksArray);
    411         Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
    412             @Override
    413             public int compare(TaskRecord lhs, TaskRecord rhs) {
    414                 final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
    415                 if (diff < 0) {
    416                     return -1;
    417                 } else if (diff > 0) {
    418                     return +1;
    419                 } else {
    420                     return 0;
    421                 }
    422             }
    423         });
    424 
    425         return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
    426     }
    427 
    428     private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
    429         if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
    430                 " files=" + files);
    431         if (files == null) {
    432             Slog.e(TAG, "File error accessing recents directory (too many files open?).");
    433             return;
    434         }
    435         for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
    436             File file = files[fileNdx];
    437             String filename = file.getName();
    438             final int taskIdEnd = filename.indexOf('_');
    439             if (taskIdEnd > 0) {
    440                 final int taskId;
    441                 try {
    442                     taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
    443                     if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
    444                 } catch (Exception e) {
    445                     Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
    446                     file.delete();
    447                     continue;
    448                 }
    449                 if (!persistentTaskIds.contains(taskId)) {
    450                     if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
    451                     file.delete();
    452                 }
    453             }
    454         }
    455     }
    456 
    457     private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
    458         removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
    459         removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
    460     }
    461 
    462     static Bitmap restoreImage(String filename) {
    463         if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
    464         return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
    465     }
    466 
    467     private class LazyTaskWriterThread extends Thread {
    468 
    469         LazyTaskWriterThread(String name) {
    470             super(name);
    471         }
    472 
    473         @Override
    474         public void run() {
    475             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    476             ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
    477             while (true) {
    478                 // We can't lock mService while holding TaskPersister.this, but we don't want to
    479                 // call removeObsoleteFiles every time through the loop, only the last time before
    480                 // going to sleep. The risk is that we call removeObsoleteFiles() successively.
    481                 final boolean probablyDone;
    482                 synchronized (TaskPersister.this) {
    483                     probablyDone = mWriteQueue.isEmpty();
    484                 }
    485                 if (probablyDone) {
    486                     if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
    487                     persistentTaskIds.clear();
    488                     synchronized (mService) {
    489                         if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
    490                         for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
    491                             final TaskRecord task = mRecentTasks.get(taskNdx);
    492                             if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
    493                                     " persistable=" + task.isPersistable);
    494                             if ((task.isPersistable || task.inRecents)
    495                                     && (task.stack == null || !task.stack.isHomeStack())) {
    496                                 if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
    497                                 persistentTaskIds.add(task.taskId);
    498                             } else {
    499                                 if (DEBUG) Slog.d(TAG,
    500                                         "omitting from persistentTaskIds task=" + task);
    501                             }
    502                         }
    503                     }
    504                     removeObsoleteFiles(persistentTaskIds);
    505                 }
    506 
    507                 // If mNextWriteTime, then don't delay between each call to saveToXml().
    508                 final WriteQueueItem item;
    509                 synchronized (TaskPersister.this) {
    510                     if (mNextWriteTime != FLUSH_QUEUE) {
    511                         // The next write we don't have to wait so long.
    512                         mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
    513                         if (DEBUG) Slog.d(TAG, "Next write time may be in " +
    514                                 INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
    515                     }
    516 
    517 
    518                     while (mWriteQueue.isEmpty()) {
    519                         if (mNextWriteTime != 0) {
    520                             mNextWriteTime = 0; // idle.
    521                             TaskPersister.this.notifyAll(); // wake up flush() if needed.
    522                         }
    523                         try {
    524                             if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
    525                             TaskPersister.this.wait();
    526                         } catch (InterruptedException e) {
    527                         }
    528                         // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
    529                         // from now.
    530                     }
    531                     item = mWriteQueue.remove(0);
    532 
    533                     long now = SystemClock.uptimeMillis();
    534                     if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
    535                             mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
    536                     while (now < mNextWriteTime) {
    537                         try {
    538                             if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
    539                                     (mNextWriteTime - now));
    540                             TaskPersister.this.wait(mNextWriteTime - now);
    541                         } catch (InterruptedException e) {
    542                         }
    543                         now = SystemClock.uptimeMillis();
    544                     }
    545 
    546                     // Got something to do.
    547                 }
    548 
    549                 if (item instanceof ImageWriteQueueItem) {
    550                     ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
    551                     final String filename = imageWriteQueueItem.mFilename;
    552                     final Bitmap bitmap = imageWriteQueueItem.mImage;
    553                     if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
    554                     FileOutputStream imageFile = null;
    555                     try {
    556                         imageFile = new FileOutputStream(new File(sImagesDir, filename));
    557                         bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
    558                     } catch (Exception e) {
    559                         Slog.e(TAG, "saveImage: unable to save " + filename, e);
    560                     } finally {
    561                         IoUtils.closeQuietly(imageFile);
    562                     }
    563                 } else if (item instanceof TaskWriteQueueItem) {
    564                     // Write out one task.
    565                     StringWriter stringWriter = null;
    566                     TaskRecord task = ((TaskWriteQueueItem) item).mTask;
    567                     if (DEBUG) Slog.d(TAG, "Writing task=" + task);
    568                     synchronized (mService) {
    569                         if (task.inRecents) {
    570                             // Still there.
    571                             try {
    572                                 if (DEBUG) Slog.d(TAG, "Saving task=" + task);
    573                                 stringWriter = saveToXml(task);
    574                             } catch (IOException e) {
    575                             } catch (XmlPullParserException e) {
    576                             }
    577                         }
    578                     }
    579                     if (stringWriter != null) {
    580                         // Write out xml file while not holding mService lock.
    581                         FileOutputStream file = null;
    582                         AtomicFile atomicFile = null;
    583                         try {
    584                             atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(
    585                                     task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
    586                             file = atomicFile.startWrite();
    587                             file.write(stringWriter.toString().getBytes());
    588                             file.write('\n');
    589                             atomicFile.finishWrite(file);
    590                         } catch (IOException e) {
    591                             if (file != null) {
    592                                 atomicFile.failWrite(file);
    593                             }
    594                             Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +
    595                                     e);
    596                         }
    597                     }
    598                 }
    599             }
    600         }
    601     }
    602 }
    603