TaskPersisterTest.java revision b258f6a4bd685e5efcb36c02d5817f659e10479b
1/*
2 * Copyright (C) 2016 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
19import android.app.ActivityManager;
20import android.content.ContentResolver;
21import android.content.pm.ActivityInfo;
22import android.content.pm.UserInfo;
23import android.os.Environment;
24import android.os.SystemClock;
25import android.os.UserHandle;
26import android.os.UserManager;
27import android.provider.Settings;
28import android.test.AndroidTestCase;
29import android.util.Log;
30import android.util.SparseBooleanArray;
31import android.util.Xml;
32
33import com.android.internal.util.FastXmlSerializer;
34import com.android.internal.util.XmlUtils;
35import com.android.server.am.TaskPersister;
36
37import java.io.File;
38import java.io.IOException;
39import java.io.StringReader;
40import java.io.StringWriter;
41import java.util.Random;
42
43import libcore.io.IoUtils;
44
45import org.xmlpull.v1.XmlPullParser;
46import org.xmlpull.v1.XmlPullParserException;
47import org.xmlpull.v1.XmlSerializer;
48
49public class TaskPersisterTest extends AndroidTestCase {
50    private static final String TEST_USER_NAME = "AM-Test-User";
51
52    private TaskPersister mTaskPersister;
53    private int testUserId;
54    private UserManager mUserManager;
55
56    @Override
57    public void setUp() throws Exception {
58        super.setUp();
59        mUserManager = UserManager.get(getContext());
60        mTaskPersister = new TaskPersister(getContext().getFilesDir());
61        testUserId = createUser(TEST_USER_NAME, 0);
62    }
63
64    @Override
65    public void tearDown() throws Exception {
66        super.tearDown();
67        mTaskPersister.unloadUserDataFromMemory(testUserId);
68        removeUser(testUserId);
69    }
70
71    private int getRandomTaskIdForUser(int userId) {
72        int taskId = (int) (Math.random() * UserHandle.PER_USER_RANGE);
73        taskId += UserHandle.PER_USER_RANGE * userId;
74        return taskId;
75    }
76
77    public void testTaskIdsPersistence() {
78        SparseBooleanArray taskIdsOnFile = mTaskPersister.loadPersistedTaskIdsForUser(testUserId);
79        for (int i = 0; i < 100; i++) {
80            taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true);
81        }
82        mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
83        SparseBooleanArray newTaskIdsOnFile = mTaskPersister
84                .loadPersistedTaskIdsForUser(testUserId);
85        assertTrue("TaskIds written differ from TaskIds read back from file",
86                taskIdsOnFile.equals(newTaskIdsOnFile));
87    }
88
89    public void testActiveTimeMigration() {
90        // Simulate a migration scenario by setting the last write uptime to zero
91        ContentResolver cr = getContext().getContentResolver();
92        Settings.Secure.putLong(cr,
93                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, 0);
94
95        // Create a dummy task record with an absolute time 1s before now
96        long pastOffset = 1000;
97        long activeTime = System.currentTimeMillis() - pastOffset;
98        TaskRecord tr0 = createDummyTaskRecordWithActiveTime(activeTime, activeTime);
99
100        // Save and load the tasks with no last persist uptime (0)
101        String tr0XmlStr = serializeTaskRecordToXmlString(tr0);
102        TaskRecord xtr0 = unserializeTaskRecordFromXmlString(tr0XmlStr, 0);
103
104        // Ensure that the absolute time has been migrated to be relative to the current elapsed
105        // time
106        assertTrue("Expected firstActiveTime to be migrated from: " + tr0.firstActiveTime +
107                " instead found: " + xtr0.firstActiveTime,
108                        xtr0.firstActiveTime <= -pastOffset);
109        assertTrue("Expected lastActiveTime to be migrated from: " + tr0.lastActiveTime +
110                " instead found: " + xtr0.lastActiveTime,
111                        xtr0.lastActiveTime <= -pastOffset);
112
113        // Ensure that the last active uptime is not set so that SystemUI can migrate it itself
114        // assuming that the last persist time is zero
115        Settings.Secure.putLongForUser(cr,
116                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, 0, testUserId);
117        mTaskPersister.restoreTasksForUserLocked(testUserId);
118        long lastVisTaskActiveTime = Settings.Secure.getLongForUser(cr,
119                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, -1, testUserId);
120        assertTrue("Expected last visible task active time is zero", lastVisTaskActiveTime == 0);
121    }
122
123    public void testActiveTimeOffsets() {
124        // Simulate a normal boot scenario by setting the last write uptime
125        long lastWritePastOffset = 1000;
126        long lastVisActivePastOffset = 500;
127        ContentResolver cr = getContext().getContentResolver();
128        Settings.Secure.putLong(cr,
129                Settings.Secure.TASK_PERSISTER_LAST_WRITE_UPTIME, lastWritePastOffset);
130
131        // Create a dummy task record with an absolute time 1s before now
132        long activeTime = 250;
133        TaskRecord tr0 = createDummyTaskRecordWithActiveTime(activeTime, activeTime);
134
135        // Save and load the tasks with the last persist time
136        String tr0XmlStr = serializeTaskRecordToXmlString(tr0);
137        TaskRecord xtr0 = unserializeTaskRecordFromXmlString(tr0XmlStr, lastWritePastOffset);
138
139        // Ensure that the prior elapsed time has been offset to be relative to the current boot
140        // time
141        assertTrue("Expected firstActiveTime to be offset from: " + tr0.firstActiveTime +
142                " instead found: " + xtr0.firstActiveTime,
143                        xtr0.firstActiveTime <= (-lastWritePastOffset + activeTime));
144        assertTrue("Expected lastActiveTime to be offset from: " + tr0.lastActiveTime +
145                " instead found: " + xtr0.lastActiveTime,
146                        xtr0.lastActiveTime <= (-lastWritePastOffset + activeTime));
147
148        // Ensure that we update the last active uptime as well by simulating a restoreTasks call
149        Settings.Secure.putLongForUser(cr,
150                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, lastVisActivePastOffset,
151                        testUserId);
152        mTaskPersister.restoreTasksForUserLocked(testUserId);
153        long lastVisTaskActiveTime = Settings.Secure.getLongForUser(cr,
154                Settings.Secure.OVERVIEW_LAST_VISIBLE_TASK_ACTIVE_UPTIME, Long.MAX_VALUE,
155                        testUserId);
156        assertTrue("Expected last visible task active time to be offset", lastVisTaskActiveTime <=
157                (-lastWritePastOffset + lastVisActivePastOffset));
158    }
159
160    private TaskRecord createDummyTaskRecordWithActiveTime(long firstActiveTime,
161            long lastActiveTime) {
162        ActivityInfo info = createDummyActivityInfo();
163        ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
164        TaskRecord t = new TaskRecord(null, 0, info, null, td, null);
165        t.firstActiveTime = firstActiveTime;
166        t.lastActiveTime = lastActiveTime;
167        return t;
168    }
169
170    private ActivityInfo createDummyActivityInfo() {
171        ActivityInfo info = new ActivityInfo();
172        info.applicationInfo = getContext().getApplicationInfo();
173        return info;
174    }
175
176    private String serializeTaskRecordToXmlString(TaskRecord tr) {
177        StringWriter stringWriter = new StringWriter();
178
179        try {
180            final XmlSerializer xmlSerializer = new FastXmlSerializer();
181            xmlSerializer.setOutput(stringWriter);
182
183            xmlSerializer.startDocument(null, true);
184            xmlSerializer.startTag(null, TaskPersister.TAG_TASK);
185            tr.saveToXml(xmlSerializer);
186            xmlSerializer.endTag(null, TaskPersister.TAG_TASK);
187            xmlSerializer.endDocument();
188            xmlSerializer.flush();
189        } catch (Exception e) {
190            e.printStackTrace();
191        }
192
193        return stringWriter.toString();
194    }
195
196    private TaskRecord unserializeTaskRecordFromXmlString(String xmlStr, long lastPersistUptime) {
197        StringReader reader = null;
198        TaskRecord task = null;
199        try {
200            reader = new StringReader(xmlStr);
201            final XmlPullParser in = Xml.newPullParser();
202            in.setInput(reader);
203
204            int event;
205            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
206                    event != XmlPullParser.END_TAG) {
207                final String name = in.getName();
208                if (event == XmlPullParser.START_TAG) {
209                    if (TaskPersister.TAG_TASK.equals(name)) {
210                        task = TaskRecord.restoreFromXml(in, null, null, lastPersistUptime);
211                    }
212                }
213                XmlUtils.skipCurrentTag(in);
214            }
215        } catch (Exception e) {
216            return null;
217        } finally {
218            IoUtils.closeQuietly(reader);
219        }
220        return task;
221    }
222
223    private int createUser(String name, int flags) {
224        UserInfo user = mUserManager.createUser(name, flags);
225        if (user == null) {
226            fail("Error while creating the test user: " + TEST_USER_NAME);
227        }
228        return user.id;
229    }
230
231    private void removeUser(int userId) {
232        if (!mUserManager.removeUser(userId)) {
233            fail("Error while removing the test user: " + TEST_USER_NAME);
234        }
235    }
236}