blob: 150baf0e89d59983611c804565834301824cb4c2 [file] [log] [blame]
Craig Mautner21d24a22014-04-23 11:45:37 -07001/*
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
17package com.android.server.am;
18
Wale Ogunwale18795a22014-12-03 11:38:33 -080019import android.content.pm.IPackageManager;
Craig Mautner21d24a22014-04-23 11:45:37 -070020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.os.Debug;
23import android.os.SystemClock;
24import android.util.ArraySet;
25import android.util.AtomicFile;
26import android.util.Slog;
27import android.util.Xml;
Riley Andrewsf16c2e82015-06-02 18:24:48 -070028import android.os.Process;
Wale Ogunwale18795a22014-12-03 11:38:33 -080029
Amith Yamasani515d4062015-09-28 11:30:06 -070030import com.android.internal.util.ArrayUtils;
Craig Mautner21d24a22014-04-23 11:45:37 -070031import com.android.internal.util.FastXmlSerializer;
32import com.android.internal.util.XmlUtils;
Wale Ogunwale18795a22014-12-03 11:38:33 -080033
Craig Mautner21d24a22014-04-23 11:45:37 -070034import org.xmlpull.v1.XmlPullParser;
35import org.xmlpull.v1.XmlPullParserException;
36import org.xmlpull.v1.XmlSerializer;
37
38import java.io.BufferedReader;
39import java.io.File;
40import java.io.FileOutputStream;
41import java.io.FileReader;
42import java.io.IOException;
43import java.io.StringWriter;
44import java.util.ArrayList;
45import java.util.Arrays;
46import java.util.Comparator;
Wale Ogunwale18795a22014-12-03 11:38:33 -080047
48import libcore.io.IoUtils;
49
Craig Mautner21d24a22014-04-23 11:45:37 -070050public class TaskPersister {
51 static final String TAG = "TaskPersister";
Stefan Kuhnee88d1e52015-05-18 10:33:45 -070052 static final boolean DEBUG = false;
Craig Mautner21d24a22014-04-23 11:45:37 -070053
Craig Mautnerf4f8bb72014-07-29 10:41:40 -070054 /** When not flushing don't write out files faster than this */
55 private static final long INTER_WRITE_DELAY_MS = 500;
56
57 /** When not flushing delay this long before writing the first file out. This gives the next
58 * task being launched a chance to load its resources without this occupying IO bandwidth. */
59 private static final long PRE_TASK_DELAY_MS = 3000;
Craig Mautner21d24a22014-04-23 11:45:37 -070060
Craig Mautner63f10902014-09-16 23:57:21 -070061 /** The maximum number of entries to keep in the queue before draining it automatically. */
62 private static final int MAX_WRITE_QUEUE_LENGTH = 6;
63
64 /** Special value for mWriteTime to mean don't wait, just write */
65 private static final long FLUSH_QUEUE = -1;
66
Craig Mautner21d24a22014-04-23 11:45:37 -070067 private static final String RECENTS_FILENAME = "_task";
68 private static final String TASKS_DIRNAME = "recent_tasks";
69 private static final String TASK_EXTENSION = ".xml";
70 private static final String IMAGES_DIRNAME = "recent_images";
Craig Mautnerc0ffce52014-07-01 12:38:52 -070071 static final String IMAGE_EXTENSION = ".png";
Craig Mautner21d24a22014-04-23 11:45:37 -070072
73 private static final String TAG_TASK = "task";
74
Craig Mautnerc0ffce52014-07-01 12:38:52 -070075 static File sImagesDir;
76 static File sTasksDir;
Craig Mautner21d24a22014-04-23 11:45:37 -070077
78 private final ActivityManagerService mService;
79 private final ActivityStackSupervisor mStackSupervisor;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -080080 private final RecentTasks mRecentTasks;
Craig Mautner21d24a22014-04-23 11:45:37 -070081
Craig Mautnerf4f8bb72014-07-29 10:41:40 -070082 /** Value determines write delay mode as follows:
83 * < 0 We are Flushing. No delays between writes until the image queue is drained and all
84 * tasks needing persisting are written to disk. There is no delay between writes.
85 * == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS.
86 * > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be
87 * delayed by #INTER_WRITE_DELAY_MS. */
88 private long mNextWriteTime = 0;
Craig Mautner21d24a22014-04-23 11:45:37 -070089
90 private final LazyTaskWriterThread mLazyTaskWriterThread;
91
Craig Mautnerf4f8bb72014-07-29 10:41:40 -070092 private static class WriteQueueItem {}
93 private static class TaskWriteQueueItem extends WriteQueueItem {
94 final TaskRecord mTask;
Winsonc809cbb2015-11-02 12:06:15 -080095
Craig Mautnerf4f8bb72014-07-29 10:41:40 -070096 TaskWriteQueueItem(TaskRecord task) {
97 mTask = task;
98 }
99 }
100 private static class ImageWriteQueueItem extends WriteQueueItem {
101 final String mFilename;
102 Bitmap mImage;
Winsonc809cbb2015-11-02 12:06:15 -0800103
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700104 ImageWriteQueueItem(String filename, Bitmap image) {
105 mFilename = filename;
106 mImage = image;
107 }
108 }
109
110 ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
111
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800112 TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
113 RecentTasks recentTasks) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700114 sTasksDir = new File(systemDir, TASKS_DIRNAME);
115 if (!sTasksDir.exists()) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700116 if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
Craig Mautner21d24a22014-04-23 11:45:37 -0700117 if (!sTasksDir.mkdir()) {
118 Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
119 }
120 }
121
122 sImagesDir = new File(systemDir, IMAGES_DIRNAME);
123 if (!sImagesDir.exists()) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700124 if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
Craig Mautner21d24a22014-04-23 11:45:37 -0700125 if (!sImagesDir.mkdir()) {
126 Slog.e(TAG, "Failure creating images directory " + sImagesDir);
127 }
128 }
129
130 mStackSupervisor = stackSupervisor;
131 mService = stackSupervisor.mService;
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800132 mRecentTasks = recentTasks;
Craig Mautner21d24a22014-04-23 11:45:37 -0700133 mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
134 }
135
136 void startPersisting() {
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800137 if (!mLazyTaskWriterThread.isAlive()) {
138 mLazyTaskWriterThread.start();
139 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700140 }
141
Craig Mautner63f10902014-09-16 23:57:21 -0700142 private void removeThumbnails(TaskRecord task) {
143 final String taskString = Integer.toString(task.taskId);
144 for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
145 final WriteQueueItem item = mWriteQueue.get(queueNdx);
146 if (item instanceof ImageWriteQueueItem &&
147 ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700148 if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
149 " from write queue");
Craig Mautner63f10902014-09-16 23:57:21 -0700150 mWriteQueue.remove(queueNdx);
151 }
152 }
153 }
154
155 private void yieldIfQueueTooDeep() {
156 boolean stall = false;
157 synchronized (this) {
158 if (mNextWriteTime == FLUSH_QUEUE) {
159 stall = true;
160 }
161 }
162 if (stall) {
163 Thread.yield();
164 }
165 }
166
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700167 void wakeup(TaskRecord task, boolean flush) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700168 synchronized (this) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700169 if (task != null) {
170 int queueNdx;
171 for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
172 final WriteQueueItem item = mWriteQueue.get(queueNdx);
173 if (item instanceof TaskWriteQueueItem &&
174 ((TaskWriteQueueItem) item).mTask == task) {
Craig Mautner63f10902014-09-16 23:57:21 -0700175 if (!task.inRecents) {
176 // This task is being removed.
177 removeThumbnails(task);
178 }
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700179 break;
180 }
181 }
Wale Ogunwalebe23ff42014-10-21 16:29:51 -0700182 if (queueNdx < 0 && task.isPersistable) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700183 mWriteQueue.add(new TaskWriteQueueItem(task));
184 }
185 } else {
186 // Dummy.
187 mWriteQueue.add(new WriteQueueItem());
188 }
Craig Mautner63f10902014-09-16 23:57:21 -0700189 if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
190 mNextWriteTime = FLUSH_QUEUE;
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700191 } else if (mNextWriteTime == 0) {
192 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
193 }
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700194 if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
195 + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
196 + " Callers=" + Debug.getCallers(4));
Craig Mautner21d24a22014-04-23 11:45:37 -0700197 notifyAll();
198 }
Craig Mautner63f10902014-09-16 23:57:21 -0700199
200 yieldIfQueueTooDeep();
Craig Mautner21d24a22014-04-23 11:45:37 -0700201 }
202
Dianne Hackbornce0fd762014-09-19 12:58:15 -0700203 void flush() {
204 synchronized (this) {
205 mNextWriteTime = FLUSH_QUEUE;
206 notifyAll();
207 do {
208 try {
209 wait();
210 } catch (InterruptedException e) {
211 }
212 } while (mNextWriteTime == FLUSH_QUEUE);
213 }
214 }
215
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700216 void saveImage(Bitmap image, String filename) {
217 synchronized (this) {
218 int queueNdx;
219 for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
220 final WriteQueueItem item = mWriteQueue.get(queueNdx);
221 if (item instanceof ImageWriteQueueItem) {
222 ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
223 if (imageWriteQueueItem.mFilename.equals(filename)) {
224 // replace the Bitmap with the new one.
225 imageWriteQueueItem.mImage = image;
226 break;
227 }
228 }
229 }
230 if (queueNdx < 0) {
231 mWriteQueue.add(new ImageWriteQueueItem(filename, image));
232 }
Craig Mautner63f10902014-09-16 23:57:21 -0700233 if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
234 mNextWriteTime = FLUSH_QUEUE;
235 } else if (mNextWriteTime == 0) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700236 mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
237 }
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700238 if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700239 SystemClock.uptimeMillis() + " mNextWriteTime=" +
240 mNextWriteTime + " Callers=" + Debug.getCallers(4));
241 notifyAll();
242 }
Craig Mautner63f10902014-09-16 23:57:21 -0700243
244 yieldIfQueueTooDeep();
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700245 }
246
Craig Mautner648f69b2014-09-18 14:16:26 -0700247 Bitmap getTaskDescriptionIcon(String filename) {
248 // See if it is in the write queue
249 final Bitmap icon = getImageFromWriteQueue(filename);
250 if (icon != null) {
251 return icon;
252 }
253 return restoreImage(filename);
254 }
255
256 Bitmap getImageFromWriteQueue(String filename) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700257 synchronized (this) {
258 for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
259 final WriteQueueItem item = mWriteQueue.get(queueNdx);
260 if (item instanceof ImageWriteQueueItem) {
261 ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
262 if (imageWriteQueueItem.mFilename.equals(filename)) {
263 return imageWriteQueueItem.mImage;
264 }
265 }
266 }
267 return null;
268 }
269 }
270
Craig Mautner21d24a22014-04-23 11:45:37 -0700271 private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700272 if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
Craig Mautner21d24a22014-04-23 11:45:37 -0700273 final XmlSerializer xmlSerializer = new FastXmlSerializer();
274 StringWriter stringWriter = new StringWriter();
275 xmlSerializer.setOutput(stringWriter);
276
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700277 if (DEBUG) xmlSerializer.setFeature(
Craig Mautner21d24a22014-04-23 11:45:37 -0700278 "http://xmlpull.org/v1/doc/features.html#indent-output", true);
279
280 // save task
281 xmlSerializer.startDocument(null, true);
282
283 xmlSerializer.startTag(null, TAG_TASK);
284 task.saveToXml(xmlSerializer);
285 xmlSerializer.endTag(null, TAG_TASK);
286
287 xmlSerializer.endDocument();
288 xmlSerializer.flush();
289
290 return stringWriter;
291 }
292
Craig Mautner77b04262014-06-27 15:22:12 -0700293 private String fileToString(File file) {
294 final String newline = System.lineSeparator();
295 try {
296 BufferedReader reader = new BufferedReader(new FileReader(file));
297 StringBuffer sb = new StringBuffer((int) file.length() * 2);
298 String line;
299 while ((line = reader.readLine()) != null) {
300 sb.append(line + newline);
301 }
302 reader.close();
303 return sb.toString();
304 } catch (IOException ioe) {
305 Slog.e(TAG, "Couldn't read file " + file.getName());
306 return null;
307 }
308 }
309
Craig Mautnera228ae92014-07-09 05:44:55 -0700310 private TaskRecord taskIdToTask(int taskId, ArrayList<TaskRecord> tasks) {
311 if (taskId < 0) {
312 return null;
313 }
314 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
315 final TaskRecord task = tasks.get(taskNdx);
316 if (task.taskId == taskId) {
317 return task;
318 }
319 }
320 Slog.e(TAG, "Restore affiliation error looking for taskId=" + taskId);
321 return null;
322 }
323
Amith Yamasani515d4062015-09-28 11:30:06 -0700324 ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700325 final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
326 ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
327
328 File[] recentFiles = sTasksDir.listFiles();
329 if (recentFiles == null) {
330 Slog.e(TAG, "Unable to list files from " + sTasksDir);
331 return tasks;
332 }
333
334 for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
335 File taskFile = recentFiles[taskNdx];
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700336 if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
Craig Mautner21d24a22014-04-23 11:45:37 -0700337 BufferedReader reader = null;
Craig Mautnere0129b32014-05-25 16:41:09 -0700338 boolean deleteFile = false;
Craig Mautner21d24a22014-04-23 11:45:37 -0700339 try {
340 reader = new BufferedReader(new FileReader(taskFile));
341 final XmlPullParser in = Xml.newPullParser();
342 in.setInput(reader);
343
344 int event;
345 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
346 event != XmlPullParser.END_TAG) {
347 final String name = in.getName();
348 if (event == XmlPullParser.START_TAG) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700349 if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
Craig Mautner21d24a22014-04-23 11:45:37 -0700350 if (TAG_TASK.equals(name)) {
351 final TaskRecord task =
352 TaskRecord.restoreFromXml(in, mStackSupervisor);
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700353 if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
Craig Mautner77b04262014-06-27 15:22:12 -0700354 task);
Craig Mautner21d24a22014-04-23 11:45:37 -0700355 if (task != null) {
Dianne Hackborn852975d2014-08-22 17:42:43 -0700356 // XXX Don't add to write queue... there is no reason to write
357 // out the stuff we just read, if we don't write it we will
358 // read the same thing again.
359 //mWriteQueue.add(new TaskWriteQueueItem(task));
Craig Mautner21d24a22014-04-23 11:45:37 -0700360 final int taskId = task.taskId;
Craig Mautner21d24a22014-04-23 11:45:37 -0700361 mStackSupervisor.setNextTaskId(taskId);
Amith Yamasani515d4062015-09-28 11:30:06 -0700362 // Check if it's a valid user id. Don't add tasks for removed users.
363 if (ArrayUtils.contains(validUserIds, task.userId)) {
364 task.isPersistable = true;
365 tasks.add(task);
366 recoveredTaskIds.add(taskId);
367 }
Craig Mautner77b04262014-06-27 15:22:12 -0700368 } else {
369 Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
370 fileToString(taskFile));
Craig Mautner21d24a22014-04-23 11:45:37 -0700371 }
372 } else {
Craig Mautner43e52ed2014-06-16 17:18:52 -0700373 Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
374 " name=" + name);
Craig Mautner21d24a22014-04-23 11:45:37 -0700375 }
376 }
377 XmlUtils.skipCurrentTag(in);
378 }
Craig Mautnere0129b32014-05-25 16:41:09 -0700379 } catch (Exception e) {
Craig Mautnera228ae92014-07-09 05:44:55 -0700380 Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
Craig Mautner77b04262014-06-27 15:22:12 -0700381 Slog.e(TAG, "Failing file: " + fileToString(taskFile));
Craig Mautnere0129b32014-05-25 16:41:09 -0700382 deleteFile = true;
Craig Mautner21d24a22014-04-23 11:45:37 -0700383 } finally {
Wale Ogunwale18795a22014-12-03 11:38:33 -0800384 IoUtils.closeQuietly(reader);
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700385 if (deleteFile) {
386 if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
Craig Mautnere0129b32014-05-25 16:41:09 -0700387 taskFile.delete();
388 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700389 }
390 }
391
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700392 if (!DEBUG) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700393 removeObsoleteFiles(recoveredTaskIds);
394 }
395
Craig Mautnera228ae92014-07-09 05:44:55 -0700396 // Fixup task affiliation from taskIds
397 for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
398 final TaskRecord task = tasks.get(taskNdx);
399 task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
400 task.setNextAffiliate(taskIdToTask(task.mNextAffiliateTaskId, tasks));
401 }
402
Craig Mautner21d24a22014-04-23 11:45:37 -0700403 TaskRecord[] tasksArray = new TaskRecord[tasks.size()];
404 tasks.toArray(tasksArray);
405 Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
406 @Override
407 public int compare(TaskRecord lhs, TaskRecord rhs) {
Craig Mautner43e52ed2014-06-16 17:18:52 -0700408 final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
Craig Mautner21d24a22014-04-23 11:45:37 -0700409 if (diff < 0) {
410 return -1;
411 } else if (diff > 0) {
412 return +1;
413 } else {
414 return 0;
415 }
416 }
417 });
418
419 return new ArrayList<TaskRecord>(Arrays.asList(tasksArray));
420 }
421
Craig Mautnere0129b32014-05-25 16:41:09 -0700422 private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700423 if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
424 " files=" + files);
Craig Mautnera5badf02014-09-11 12:47:03 -0700425 if (files == null) {
426 Slog.e(TAG, "File error accessing recents directory (too many files open?).");
427 return;
428 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700429 for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
430 File file = files[fileNdx];
431 String filename = file.getName();
Craig Mautnerffcfcaa2014-06-05 09:54:38 -0700432 final int taskIdEnd = filename.indexOf('_');
Craig Mautner21d24a22014-04-23 11:45:37 -0700433 if (taskIdEnd > 0) {
434 final int taskId;
435 try {
436 taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700437 if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
Craig Mautner21d24a22014-04-23 11:45:37 -0700438 } catch (Exception e) {
Craig Mautner43e52ed2014-06-16 17:18:52 -0700439 Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
Craig Mautner21d24a22014-04-23 11:45:37 -0700440 file.delete();
441 continue;
442 }
443 if (!persistentTaskIds.contains(taskId)) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700444 if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
Craig Mautner21d24a22014-04-23 11:45:37 -0700445 file.delete();
446 }
447 }
448 }
449 }
450
451 private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
452 removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
453 removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
454 }
455
456 static Bitmap restoreImage(String filename) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700457 if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
Craig Mautnerc0ffce52014-07-01 12:38:52 -0700458 return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
Craig Mautner21d24a22014-04-23 11:45:37 -0700459 }
460
461 private class LazyTaskWriterThread extends Thread {
Craig Mautner21d24a22014-04-23 11:45:37 -0700462
463 LazyTaskWriterThread(String name) {
464 super(name);
465 }
466
467 @Override
468 public void run() {
Riley Andrewsf16c2e82015-06-02 18:24:48 -0700469 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Craig Mautner21d24a22014-04-23 11:45:37 -0700470 ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
471 while (true) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700472 // We can't lock mService while holding TaskPersister.this, but we don't want to
473 // call removeObsoleteFiles every time through the loop, only the last time before
474 // going to sleep. The risk is that we call removeObsoleteFiles() successively.
475 final boolean probablyDone;
Craig Mautner21d24a22014-04-23 11:45:37 -0700476 synchronized (TaskPersister.this) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700477 probablyDone = mWriteQueue.isEmpty();
478 }
479 if (probablyDone) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700480 if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700481 persistentTaskIds.clear();
482 synchronized (mService) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700483 if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
Wale Ogunwalec82f2f52014-12-09 09:32:50 -0800484 for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
485 final TaskRecord task = mRecentTasks.get(taskNdx);
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700486 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
Wale Ogunwalebe23ff42014-10-21 16:29:51 -0700487 " persistable=" + task.isPersistable);
488 if ((task.isPersistable || task.inRecents)
Wale Ogunwale18795a22014-12-03 11:38:33 -0800489 && (task.stack == null || !task.stack.isHomeStack())) {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700490 if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700491 persistentTaskIds.add(task.taskId);
492 } else {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700493 if (DEBUG) Slog.d(TAG,
Wale Ogunwalebe23ff42014-10-21 16:29:51 -0700494 "omitting from persistentTaskIds task=" + task);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700495 }
496 }
497 }
498 removeObsoleteFiles(persistentTaskIds);
499 }
500
501 // If mNextWriteTime, then don't delay between each call to saveToXml().
502 final WriteQueueItem item;
503 synchronized (TaskPersister.this) {
Craig Mautner63f10902014-09-16 23:57:21 -0700504 if (mNextWriteTime != FLUSH_QUEUE) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700505 // The next write we don't have to wait so long.
506 mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700507 if (DEBUG) Slog.d(TAG, "Next write time may be in " +
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700508 INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
509 }
510
Dianne Hackbornce0fd762014-09-19 12:58:15 -0700511
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700512 while (mWriteQueue.isEmpty()) {
Dianne Hackbornce0fd762014-09-19 12:58:15 -0700513 if (mNextWriteTime != 0) {
514 mNextWriteTime = 0; // idle.
515 TaskPersister.this.notifyAll(); // wake up flush() if needed.
516 }
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700517 try {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700518 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700519 TaskPersister.this.wait();
520 } catch (InterruptedException e) {
521 }
Craig Mautner63f10902014-09-16 23:57:21 -0700522 // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
523 // from now.
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700524 }
525 item = mWriteQueue.remove(0);
526
Craig Mautner21d24a22014-04-23 11:45:37 -0700527 long now = SystemClock.uptimeMillis();
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700528 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
529 mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700530 while (now < mNextWriteTime) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700531 try {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700532 if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700533 (mNextWriteTime - now));
534 TaskPersister.this.wait(mNextWriteTime - now);
Craig Mautner21d24a22014-04-23 11:45:37 -0700535 } catch (InterruptedException e) {
536 }
537 now = SystemClock.uptimeMillis();
538 }
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700539
540 // Got something to do.
Craig Mautner21d24a22014-04-23 11:45:37 -0700541 }
542
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700543 if (item instanceof ImageWriteQueueItem) {
544 ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
545 final String filename = imageWriteQueueItem.mFilename;
546 final Bitmap bitmap = imageWriteQueueItem.mImage;
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700547 if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
Craig Mautnerc0ffce52014-07-01 12:38:52 -0700548 FileOutputStream imageFile = null;
549 try {
550 imageFile = new FileOutputStream(new File(sImagesDir, filename));
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700551 bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
Craig Mautnerc0ffce52014-07-01 12:38:52 -0700552 } catch (Exception e) {
553 Slog.e(TAG, "saveImage: unable to save " + filename, e);
554 } finally {
Wale Ogunwale18795a22014-12-03 11:38:33 -0800555 IoUtils.closeQuietly(imageFile);
Craig Mautnerc0ffce52014-07-01 12:38:52 -0700556 }
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700557 } else if (item instanceof TaskWriteQueueItem) {
558 // Write out one task.
559 StringWriter stringWriter = null;
560 TaskRecord task = ((TaskWriteQueueItem) item).mTask;
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700561 if (DEBUG) Slog.d(TAG, "Writing task=" + task);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700562 synchronized (mService) {
Craig Mautner63f10902014-09-16 23:57:21 -0700563 if (task.inRecents) {
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700564 // Still there.
Craig Mautner21d24a22014-04-23 11:45:37 -0700565 try {
Stefan Kuhnee88d1e52015-05-18 10:33:45 -0700566 if (DEBUG) Slog.d(TAG, "Saving task=" + task);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700567 stringWriter = saveToXml(task);
568 } catch (IOException e) {
569 } catch (XmlPullParserException e) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700570 }
571 }
Dianne Hackborn852975d2014-08-22 17:42:43 -0700572 }
573 if (stringWriter != null) {
574 // Write out xml file while not holding mService lock.
575 FileOutputStream file = null;
576 AtomicFile atomicFile = null;
577 try {
578 atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(
579 task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
580 file = atomicFile.startWrite();
581 file.write(stringWriter.toString().getBytes());
582 file.write('\n');
583 atomicFile.finishWrite(file);
584 } catch (IOException e) {
585 if (file != null) {
586 atomicFile.failWrite(file);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700587 }
Dianne Hackborn852975d2014-08-22 17:42:43 -0700588 Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +
589 e);
Craig Mautnerf4f8bb72014-07-29 10:41:40 -0700590 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700591 }
592 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700593 }
594 }
595 }
Craig Mautner21d24a22014-04-23 11:45:37 -0700596}