blob: e6bb244ef05bf8e95ceb7ce2884fec0325ca64c4 [file] [log] [blame]
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.server.usage;
18
Hui Yu03d12402018-12-06 18:00:37 -080019import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
20
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070021import static junit.framework.TestCase.fail;
22
23import static org.testng.Assert.assertEquals;
Varun Shah21c82c32019-10-22 21:25:45 -070024import static org.testng.Assert.assertFalse;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070025
Hui Yub21d59f2019-01-28 16:33:32 -080026import android.app.usage.TimeSparseArray;
Hui Yue361a232018-10-04 15:05:21 -070027import android.app.usage.UsageEvents.Event;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070028import android.app.usage.UsageStats;
29import android.app.usage.UsageStatsManager;
30import android.content.Context;
31import android.content.res.Configuration;
32import android.test.suitebuilder.annotation.SmallTest;
Hui Yub21d59f2019-01-28 16:33:32 -080033import android.util.AtomicFile;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070034
35import androidx.test.InstrumentationRegistry;
36import androidx.test.runner.AndroidJUnit4;
37
38import org.junit.Before;
Varun Shah21c82c32019-10-22 21:25:45 -070039import org.junit.Ignore;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070040import org.junit.Test;
41import org.junit.runner.RunWith;
42
43import java.io.File;
Hui Yub21d59f2019-01-28 16:33:32 -080044import java.io.FileOutputStream;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070045import java.io.IOException;
46import java.util.List;
47import java.util.Locale;
Varun Shahece1fa02019-11-06 17:52:04 -080048import java.util.Set;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070049
50@RunWith(AndroidJUnit4.class)
51@SmallTest
52public class UsageStatsDatabaseTest {
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -080053
Varun Shahe1ba9cd2019-08-27 17:11:25 -070054 private static final int MAX_TESTED_VERSION = 5;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070055 protected Context mContext;
56 private UsageStatsDatabase mUsageStatsDatabase;
57 private File mTestDir;
58
59 private IntervalStats mIntervalStats = new IntervalStats();
60 private long mEndTime = 0;
61
Michael Wachenschwanzdc466322018-10-10 23:51:51 -070062 // Key under which the payload blob is stored
63 // same as UsageStatsBackupHelper.KEY_USAGE_STATS
64 static final String KEY_USAGE_STATS = "usage_stats";
65
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070066 private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
67 new UsageStatsDatabase.StatCombiner<IntervalStats>() {
68 @Override
69 public void combine(IntervalStats stats, boolean mutable,
70 List<IntervalStats> accResult) {
71 accResult.add(stats);
72 }
73 };
74
75 @Before
76 public void setUp() {
77 mContext = InstrumentationRegistry.getTargetContext();
78 mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
79 mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
Varun Shah23033b02019-10-07 14:41:12 -070080 mUsageStatsDatabase.readMappingsLocked();
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070081 mUsageStatsDatabase.init(1);
82 populateIntervalStats();
83 clearUsageStatsFiles();
84 }
85
86 /**
87 * A debugging utility for viewing the files currently in the test directory
88 */
89 private void clearUsageStatsFiles() {
90 File[] intervalDirs = mTestDir.listFiles();
91 for (File intervalDir : intervalDirs) {
92 if (intervalDir.isDirectory()) {
93 File[] usageFiles = intervalDir.listFiles();
94 for (File f : usageFiles) {
95 f.delete();
96 }
Varun Shahece1fa02019-11-06 17:52:04 -080097 } else {
98 intervalDir.delete();
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -070099 }
100 }
101 }
102
103 /**
104 * A debugging utility for viewing the files currently in the test directory
105 */
106 private String dumpUsageStatsFiles() {
107 StringBuilder sb = new StringBuilder();
108 File[] intervalDirs = mTestDir.listFiles();
109 for (File intervalDir : intervalDirs) {
110 if (intervalDir.isDirectory()) {
111 File[] usageFiles = intervalDir.listFiles();
112 for (File f : usageFiles) {
113 sb.append(f.toString());
114 }
115 }
116 }
117 return sb.toString();
118 }
119
120 private void populateIntervalStats() {
121 final int numberOfEvents = 3000;
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700122 final int timeProgression = 23;
123 long time = System.currentTimeMillis() - (numberOfEvents*timeProgression);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700124 mIntervalStats = new IntervalStats();
125
Hui Yue361a232018-10-04 15:05:21 -0700126 mIntervalStats.majorVersion = 7;
127 mIntervalStats.minorVersion = 8;
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700128 mIntervalStats.beginTime = time;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700129 mIntervalStats.interactiveTracker.count = 2;
130 mIntervalStats.interactiveTracker.duration = 111111;
131 mIntervalStats.nonInteractiveTracker.count = 3;
132 mIntervalStats.nonInteractiveTracker.duration = 222222;
133 mIntervalStats.keyguardShownTracker.count = 4;
134 mIntervalStats.keyguardShownTracker.duration = 333333;
135 mIntervalStats.keyguardHiddenTracker.count = 5;
136 mIntervalStats.keyguardHiddenTracker.duration = 4444444;
137
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700138 for (int i = 0; i < numberOfEvents; i++) {
Hui Yue361a232018-10-04 15:05:21 -0700139 Event event = new Event();
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800140 final int packageInt = ((i / 3) % 7); //clusters of 3 events from 7 "apps"
141 event.mPackage = "fake.package.name" + packageInt;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700142 if (packageInt == 3) {
143 // Third app is an instant app
Hui Yue361a232018-10-04 15:05:21 -0700144 event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700145 }
146
Hui Yu03d12402018-12-06 18:00:37 -0800147 final int instanceId = i % 11;
148 event.mClass = ".fake.class.name" + instanceId;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700149 event.mTimeStamp = time;
Hui Yu03d12402018-12-06 18:00:37 -0800150 event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
151 event.mInstanceId = instanceId;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700152
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800153
154 final int rootPackageInt = (i % 5); // 5 "apps" start each task
155 event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
156
157 final int rootClassInt = i % 6;
158 event.mTaskRootClass = ".fake.class.name" + rootClassInt;
159
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700160 switch (event.mEventType) {
Hui Yue361a232018-10-04 15:05:21 -0700161 case Event.CONFIGURATION_CHANGE:
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700162 //empty config,
163 event.mConfiguration = new Configuration();
164 break;
Hui Yue361a232018-10-04 15:05:21 -0700165 case Event.SHORTCUT_INVOCATION:
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700166 //"random" shortcut
167 event.mShortcutId = "shortcut" + (i % 8);
168 break;
Hui Yue361a232018-10-04 15:05:21 -0700169 case Event.STANDBY_BUCKET_CHANGED:
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700170 //"random" bucket and reason
171 event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
172 break;
Hui Yue361a232018-10-04 15:05:21 -0700173 case Event.NOTIFICATION_INTERRUPTION:
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700174 //"random" channel
175 event.mNotificationChannelId = "channel" + (i % 5);
176 break;
177 }
178
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800179 mIntervalStats.addEvent(event);
Hui Yu03d12402018-12-06 18:00:37 -0800180 mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
181 event.mInstanceId);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700182
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700183 time += timeProgression; // Arbitrary progression of time
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700184 }
185 mEndTime = time;
186
187 Configuration config1 = new Configuration();
188 config1.fontScale = 3.3f;
189 config1.mcc = 4;
190 mIntervalStats.getOrCreateConfigurationStats(config1);
191
192 Configuration config2 = new Configuration();
193 config2.mnc = 5;
194 config2.setLocale(new Locale("en", "US"));
195 mIntervalStats.getOrCreateConfigurationStats(config2);
196
197 Configuration config3 = new Configuration();
198 config3.touchscreen = 6;
199 config3.keyboard = 7;
200 mIntervalStats.getOrCreateConfigurationStats(config3);
201
202 Configuration config4 = new Configuration();
203 config4.keyboardHidden = 8;
204 config4.hardKeyboardHidden = 9;
205 mIntervalStats.getOrCreateConfigurationStats(config4);
206
207 Configuration config5 = new Configuration();
208 config5.navigation = 10;
209 config5.navigationHidden = 11;
210 mIntervalStats.getOrCreateConfigurationStats(config5);
211
212 Configuration config6 = new Configuration();
213 config6.orientation = 12;
214 //Ignore screen layout, it's determined by locale
215 mIntervalStats.getOrCreateConfigurationStats(config6);
216
217 Configuration config7 = new Configuration();
218 config7.colorMode = 14;
219 config7.uiMode = 15;
220 mIntervalStats.getOrCreateConfigurationStats(config7);
221
222 Configuration config8 = new Configuration();
223 config8.screenWidthDp = 16;
224 config8.screenHeightDp = 17;
225 mIntervalStats.getOrCreateConfigurationStats(config8);
226
227 Configuration config9 = new Configuration();
228 config9.smallestScreenWidthDp = 18;
229 config9.densityDpi = 19;
230 mIntervalStats.getOrCreateConfigurationStats(config9);
231
Varun Shah95fabaf2019-05-01 12:45:37 -0700232 Configuration config10 = new Configuration();
233 final Locale locale10 = new Locale.Builder()
234 .setLocale(new Locale("zh", "CN"))
235 .setScript("Hans")
236 .build();
237 config10.setLocale(locale10);
238 mIntervalStats.getOrCreateConfigurationStats(config10);
239
240 Configuration config11 = new Configuration();
241 final Locale locale11 = new Locale.Builder()
242 .setLocale(new Locale("zh", "CN"))
243 .setScript("Hant")
244 .build();
245 config11.setLocale(locale11);
246 mIntervalStats.getOrCreateConfigurationStats(config11);
247
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700248 mIntervalStats.activeConfiguration = config9;
249 }
250
251 void compareUsageStats(UsageStats us1, UsageStats us2) {
252 assertEquals(us1.mPackageName, us2.mPackageName);
253 // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
254 // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
255 assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
Hui Yu03d12402018-12-06 18:00:37 -0800256 assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700257 assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
Hui Yu03d12402018-12-06 18:00:37 -0800258 assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
Hui Yue361a232018-10-04 15:05:21 -0700259 assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
260 assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700261 // mLaunchCount not persisted, so skipped
262 assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700263 assertEquals(us1.mChooserCounts, us2.mChooserCounts);
264 }
265
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800266 void compareUsageEvent(Event e1, Event e2, int debugId, int minVersion) {
267 switch (minVersion) {
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700268 case 5: // test fields added in version 5
269 assertEquals(e1.mPackageToken, e2.mPackageToken, "Usage event " + debugId);
270 assertEquals(e1.mClassToken, e2.mClassToken, "Usage event " + debugId);
271 assertEquals(e1.mTaskRootPackageToken, e2.mTaskRootPackageToken,
272 "Usage event " + debugId);
273 assertEquals(e1.mTaskRootClassToken, e2.mTaskRootClassToken,
274 "Usage event " + debugId);
275 switch (e1.mEventType) {
276 case Event.SHORTCUT_INVOCATION:
277 assertEquals(e1.mShortcutIdToken, e2.mShortcutIdToken,
278 "Usage event " + debugId);
279 break;
280 case Event.NOTIFICATION_INTERRUPTION:
281 assertEquals(e1.mNotificationChannelIdToken, e2.mNotificationChannelIdToken,
282 "Usage event " + debugId);
283 break;
284 }
285 // fallthrough
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800286 case 4: // test fields added in version 4
287 assertEquals(e1.mInstanceId, e2.mInstanceId, "Usage event " + debugId);
288 assertEquals(e1.mTaskRootPackage, e2.mTaskRootPackage, "Usage event " + debugId);
289 assertEquals(e1.mTaskRootClass, e2.mTaskRootClass, "Usage event " + debugId);
290 // fallthrough
291 default:
292 assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
293 assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
294 assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
295 assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
296 switch (e1.mEventType) {
297 case Event.CONFIGURATION_CHANGE:
298 assertEquals(e1.mConfiguration, e2.mConfiguration,
299 "Usage event " + debugId + e2.mConfiguration.toString());
300 break;
301 case Event.SHORTCUT_INVOCATION:
302 assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
303 break;
304 case Event.STANDBY_BUCKET_CHANGED:
305 assertEquals(e1.mBucketAndReason, e2.mBucketAndReason,
306 "Usage event " + debugId);
307 break;
308 case Event.NOTIFICATION_INTERRUPTION:
309 assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
310 "Usage event " + debugId);
311 break;
312 }
313 assertEquals(e1.mFlags, e2.mFlags);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700314 }
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700315 }
316
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800317 void compareIntervalStats(IntervalStats stats1, IntervalStats stats2, int minVersion) {
Hui Yue361a232018-10-04 15:05:21 -0700318 assertEquals(stats1.majorVersion, stats2.majorVersion);
319 assertEquals(stats1.minorVersion, stats2.minorVersion);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700320 assertEquals(stats1.beginTime, stats2.beginTime);
321 assertEquals(stats1.endTime, stats2.endTime);
322 assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count);
323 assertEquals(stats1.interactiveTracker.duration, stats2.interactiveTracker.duration);
324 assertEquals(stats1.nonInteractiveTracker.count, stats2.nonInteractiveTracker.count);
325 assertEquals(stats1.nonInteractiveTracker.duration, stats2.nonInteractiveTracker.duration);
326 assertEquals(stats1.keyguardShownTracker.count, stats2.keyguardShownTracker.count);
327 assertEquals(stats1.keyguardShownTracker.duration, stats2.keyguardShownTracker.duration);
328 assertEquals(stats1.keyguardHiddenTracker.count, stats2.keyguardHiddenTracker.count);
329 assertEquals(stats1.keyguardHiddenTracker.duration, stats2.keyguardHiddenTracker.duration);
330
331 String[] usageKey1 = stats1.packageStats.keySet().toArray(new String[0]);
332 String[] usageKey2 = stats2.packageStats.keySet().toArray(new String[0]);
333 for (int i = 0; i < usageKey1.length; i++) {
334 UsageStats usageStats1 = stats1.packageStats.get(usageKey1[i]);
335 UsageStats usageStats2 = stats2.packageStats.get(usageKey2[i]);
336 compareUsageStats(usageStats1, usageStats2);
337 }
338
339 assertEquals(stats1.configurations.size(), stats2.configurations.size());
340 Configuration[] configSet1 = stats1.configurations.keySet().toArray(new Configuration[0]);
341 for (int i = 0; i < configSet1.length; i++) {
342 if (!stats2.configurations.containsKey(configSet1[i])) {
343 Configuration[] configSet2 = stats2.configurations.keySet().toArray(
344 new Configuration[0]);
345 String debugInfo = "";
346 for (Configuration c : configSet1) {
347 debugInfo += c.toString() + "\n";
348 }
349 debugInfo += "\n";
350 for (Configuration c : configSet2) {
351 debugInfo += c.toString() + "\n";
352 }
353 fail("Config " + configSet1[i].toString()
354 + " not found in deserialized IntervalStat\n" + debugInfo);
355 }
356 }
357 assertEquals(stats1.activeConfiguration, stats2.activeConfiguration);
Hui Yuf6118b32018-12-13 15:20:43 -0800358 assertEquals(stats1.events.size(), stats2.events.size());
359 for (int i = 0; i < stats1.events.size(); i++) {
360 compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i, minVersion);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700361 }
362 }
363
364 /**
365 * Runs the Write Read test.
366 * Will write the generated IntervalStat to disk, read it from disk and compare the two
367 */
368 void runWriteReadTest(int interval) throws IOException {
369 mUsageStatsDatabase.putUsageStats(interval, mIntervalStats);
370 List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime,
371 mIntervalStatsVerifier);
372
373 assertEquals(1, stats.size());
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800374 compareIntervalStats(mIntervalStats, stats.get(0), MAX_TESTED_VERSION);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700375 }
376
377 /**
378 * Demonstrate that IntervalStats can be serialized and deserialized from disk without loss of
379 * relevant data.
380 */
381 @Test
382 public void testWriteRead() throws IOException {
383 runWriteReadTest(UsageStatsManager.INTERVAL_DAILY);
384 runWriteReadTest(UsageStatsManager.INTERVAL_WEEKLY);
385 runWriteReadTest(UsageStatsManager.INTERVAL_MONTHLY);
386 runWriteReadTest(UsageStatsManager.INTERVAL_YEARLY);
387 }
388
389 /**
390 * Runs the Version Change tests.
391 * Will write the generated IntervalStat to disk in one version format, "upgrade" to another
392 * version and read the automatically upgraded files on disk in the new file format.
393 */
394 void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
395 // Write IntervalStats to disk in old version format
396 UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
Varun Shah23033b02019-10-07 14:41:12 -0700397 prevDB.readMappingsLocked();
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700398 prevDB.init(1);
399 prevDB.putUsageStats(interval, mIntervalStats);
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700400 if (oldVersion >= 5) {
401 prevDB.writeMappingsLocked();
402 }
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700403
404 // Simulate an upgrade to a new version and read from the disk
405 UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
Varun Shah23033b02019-10-07 14:41:12 -0700406 newDB.readMappingsLocked();
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700407 newDB.init(mEndTime);
408 List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
409 mIntervalStatsVerifier);
410
411 assertEquals(1, stats.size());
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800412
413 final int minVersion = oldVersion < newVersion ? oldVersion : newVersion;
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700414 // The written and read IntervalStats should match
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800415 compareIntervalStats(mIntervalStats, stats.get(0), minVersion);
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700416 }
417
418 /**
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700419 * Runs the Backup and Restore tests.
420 * Will write the generated IntervalStat to a database and create a backup in the specified
421 * version's format. The database will then be restored from the blob and the restored
422 * interval stats will be compared to the generated stats.
423 */
424 void runBackupRestoreTest(int version) throws IOException {
425 UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir);
Varun Shah23033b02019-10-07 14:41:12 -0700426 prevDB.readMappingsLocked();
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700427 prevDB.init(1);
428 prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
429 // Create a backup with a specific version
430 byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version);
Varun Shah21c82c32019-10-22 21:25:45 -0700431 if (version >= 1 && version <= 3) {
432 assertFalse(blob != null && blob.length != 0,
433 "UsageStatsDatabase shouldn't be able to write backups as XML");
434 return;
435 }
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700436
437 clearUsageStatsFiles();
438
439 UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
Varun Shah23033b02019-10-07 14:41:12 -0700440 newDB.readMappingsLocked();
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700441 newDB.init(1);
442 // Attempt to restore the usage stats from the backup
443 newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
444 List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime,
445 mIntervalStatsVerifier);
446
Varun Shah21c82c32019-10-22 21:25:45 -0700447 if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) {
448 assertFalse(stats != null && !stats.isEmpty(),
449 "UsageStatsDatabase shouldn't be able to restore from unknown data versions");
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700450 return;
451 }
452
453 assertEquals(1, stats.size());
454
455 // Clear non backed up data from expected IntervalStats
456 mIntervalStats.activeConfiguration = null;
457 mIntervalStats.configurations.clear();
Hui Yuf6118b32018-12-13 15:20:43 -0800458 mIntervalStats.events.clear();
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700459
460 // The written and read IntervalStats should match
Michael Wachenschwanz0b4ab1f2019-01-07 13:59:10 -0800461 compareIntervalStats(mIntervalStats, stats.get(0), version);
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700462 }
463
464 /**
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700465 * Test the version upgrade from 3 to 4
Varun Shah21c82c32019-10-22 21:25:45 -0700466 *
467 * Ignored - version 3 is now deprecated.
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700468 */
Varun Shah21c82c32019-10-22 21:25:45 -0700469 @Ignore
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700470 @Test
Varun Shah21c82c32019-10-22 21:25:45 -0700471 public void ignore_testVersionUpgradeFrom3to4() throws IOException {
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700472 runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY);
473 runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY);
474 runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
475 runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
476 }
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700477
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700478 /**
479 * Test the version upgrade from 4 to 5
480 */
481 @Test
482 public void testVersionUpgradeFrom4to5() throws IOException {
483 runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_DAILY);
484 runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_WEEKLY);
485 runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_MONTHLY);
486 runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_YEARLY);
487 }
488
489 /**
490 * Test the version upgrade from 3 to 5
Varun Shah21c82c32019-10-22 21:25:45 -0700491 *
492 * Ignored - version 3 is now deprecated.
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700493 */
Varun Shah21c82c32019-10-22 21:25:45 -0700494 @Ignore
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700495 @Test
Varun Shah21c82c32019-10-22 21:25:45 -0700496 public void ignore_testVersionUpgradeFrom3to5() throws IOException {
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700497 runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_DAILY);
498 runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_WEEKLY);
499 runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_MONTHLY);
500 runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_YEARLY);
501 }
502
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700503
504 /**
Varun Shah21c82c32019-10-22 21:25:45 -0700505 * Test backup/restore
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700506 */
507 @Test
508 public void testBackupRestore() throws IOException {
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700509 runBackupRestoreTest(4);
510
Varun Shah21c82c32019-10-22 21:25:45 -0700511 // test deprecated versions
512 runBackupRestoreTest(1);
513
Michael Wachenschwanzdc466322018-10-10 23:51:51 -0700514 // test invalid backup versions as well
515 runBackupRestoreTest(0);
516 runBackupRestoreTest(99999);
517 }
Hui Yub21d59f2019-01-28 16:33:32 -0800518
519 /**
520 * Test the pruning in indexFilesLocked() that only allow up to 100 daily files, 50 weekly files
521 * , 12 monthly files, 10 yearly files.
522 */
523 @Test
524 public void testMaxFiles() throws IOException {
525 final File[] intervalDirs = new File[]{
526 new File(mTestDir, "daily"),
527 new File(mTestDir, "weekly"),
528 new File(mTestDir, "monthly"),
529 new File(mTestDir, "yearly"),
530 };
531 // Create 10 extra files under each interval dir.
532 final int extra = 10;
533 final int length = intervalDirs.length;
534 for (int i = 0; i < length; i++) {
535 final int numFiles = UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra;
536 for (int f = 0; f < numFiles; f++) {
537 final AtomicFile file = new AtomicFile(new File(intervalDirs[i], Long.toString(f)));
538 FileOutputStream fos = file.startWrite();
539 fos.write(1);
540 file.finishWrite(fos);
541 }
542 }
543 // indexFilesLocked() list files under each interval dir, if number of files are more than
544 // the max allowed files for each interval type, it deletes the lowest numbered files.
545 mUsageStatsDatabase.forceIndexFiles();
546 final int len = mUsageStatsDatabase.mSortedStatFiles.length;
547 for (int i = 0; i < len; i++) {
548 final TimeSparseArray<AtomicFile> files = mUsageStatsDatabase.mSortedStatFiles[i];
549 // The stats file for each interval type equals to max allowed.
550 assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i],
551 files.size());
552 // The highest numbered file,
553 assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra - 1,
554 files.keyAt(files.size() - 1));
555 // The lowest numbered file:
556 assertEquals(extra, files.keyAt(0));
557 }
558 }
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700559
560 private void compareObfuscatedData(int interval) throws IOException {
561 // Write IntervalStats to disk
562 UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, 5);
Varun Shah23033b02019-10-07 14:41:12 -0700563 prevDB.readMappingsLocked();
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700564 prevDB.init(1);
565 prevDB.putUsageStats(interval, mIntervalStats);
566 prevDB.writeMappingsLocked();
567
568 // Read IntervalStats from disk into a new db
569 UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, 5);
Varun Shah23033b02019-10-07 14:41:12 -0700570 newDB.readMappingsLocked();
Varun Shahe1ba9cd2019-08-27 17:11:25 -0700571 newDB.init(mEndTime);
572 List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
573 mIntervalStatsVerifier);
574
575 assertEquals(1, stats.size());
576 // The written and read IntervalStats should match
577 compareIntervalStats(mIntervalStats, stats.get(0), 5);
578 }
579
580 @Test
581 public void testObfuscation() throws IOException {
582 compareObfuscatedData(UsageStatsManager.INTERVAL_DAILY);
583 compareObfuscatedData(UsageStatsManager.INTERVAL_WEEKLY);
584 compareObfuscatedData(UsageStatsManager.INTERVAL_MONTHLY);
585 compareObfuscatedData(UsageStatsManager.INTERVAL_YEARLY);
586 }
Varun Shah23033b02019-10-07 14:41:12 -0700587
588 private void verifyPackageNotRetained(int interval) throws IOException {
589 UsageStatsDatabase db = new UsageStatsDatabase(mTestDir, 5);
590 db.readMappingsLocked();
591 db.init(1);
592 db.putUsageStats(interval, mIntervalStats);
Varun Shahece1fa02019-11-06 17:52:04 -0800593 db.writeMappingsLocked();
Varun Shah23033b02019-10-07 14:41:12 -0700594
595 final String removedPackage = "fake.package.name0";
596 // invoke handler call directly from test to remove package
597 db.onPackageRemoved(removedPackage, System.currentTimeMillis());
598
599 List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
600 mIntervalStatsVerifier);
Varun Shahece1fa02019-11-06 17:52:04 -0800601 assertEquals(1, stats.size(),
602 "Only one interval stats object should exist for the given time range.");
603 final IntervalStats stat = stats.get(0);
604 if (stat.packageStats.containsKey(removedPackage)) {
605 fail("Found removed package " + removedPackage + " in package stats.");
606 return;
607 }
608 for (int i = 0; i < stat.events.size(); i++) {
609 final Event event = stat.events.get(i);
610 if (removedPackage.equals(event.mPackage)) {
611 fail("Found an event from removed package " + removedPackage);
Varun Shah23033b02019-10-07 14:41:12 -0700612 return;
613 }
Varun Shah23033b02019-10-07 14:41:12 -0700614 }
615 }
616
617 @Test
618 public void testPackageRetention() throws IOException {
619 verifyPackageNotRetained(UsageStatsManager.INTERVAL_DAILY);
620 verifyPackageNotRetained(UsageStatsManager.INTERVAL_WEEKLY);
621 verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
622 verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
623 }
Varun Shahece1fa02019-11-06 17:52:04 -0800624
625 private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval,
626 String removedPackage) {
627 List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
628 mIntervalStatsVerifier);
629 assertEquals(1, stats.size(),
630 "Only one interval stats object should exist for the given time range.");
631 final IntervalStats stat = stats.get(0);
632 if (stat.packageStats.containsKey(removedPackage)) {
633 fail("Found removed package " + removedPackage + " in package stats.");
634 return;
635 }
636 for (int i = 0; i < stat.events.size(); i++) {
637 final Event event = stat.events.get(i);
638 if (removedPackage.equals(event.mPackage)) {
639 fail("Found an event from removed package " + removedPackage);
640 return;
641 }
642 }
643 }
644
645 private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval,
646 Set<String> installedPackages) {
647 List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
648 mIntervalStatsVerifier);
649 assertEquals(1, stats.size(),
650 "Only one interval stats object should exist for the given time range.");
651 final IntervalStats stat = stats.get(0);
652 if (!stat.packageStats.containsAll(installedPackages)) {
653 fail("Could not find some installed packages in package stats.");
654 return;
655 }
656 // attempt to find an event from each installed package
657 for (String installedPackage : installedPackages) {
658 for (int i = 0; i < stat.events.size(); i++) {
659 if (installedPackage.equals(stat.events.get(i).mPackage)) {
660 break;
661 }
662 if (i == stat.events.size() - 1) {
663 fail("Could not find any event for: " + installedPackage);
664 return;
665 }
666 }
667 }
668 }
669
670 @Test
671 public void testPackageDataIsRemoved() throws IOException {
672 UsageStatsDatabase db = new UsageStatsDatabase(mTestDir);
673 db.readMappingsLocked();
674 db.init(1);
675
676 // write stats to disk for each interval
677 db.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
678 db.putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, mIntervalStats);
679 db.putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, mIntervalStats);
680 db.putUsageStats(UsageStatsManager.INTERVAL_YEARLY, mIntervalStats);
681 db.writeMappingsLocked();
682
683 final Set<String> installedPackages = mIntervalStats.packageStats.keySet();
684 final String removedPackage = installedPackages.iterator().next();
685 installedPackages.remove(removedPackage);
686
687 // mimic a package uninstall
688 db.onPackageRemoved(removedPackage, System.currentTimeMillis());
689
690 // mimic the idle prune job being triggered
691 db.pruneUninstalledPackagesData();
692
693 // read data from disk into a new db instance
694 UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
695 newDB.readMappingsLocked();
696 newDB.init(mEndTime);
697
698 // query data for each interval and ensure data for package doesn't exist
699 verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, removedPackage);
700 verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, removedPackage);
701 verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, removedPackage);
702 verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, removedPackage);
703
704 // query data for each interval and ensure some data for installed packages exists
705 verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, installedPackages);
706 verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, installedPackages);
707 verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, installedPackages);
708 verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, installedPackages);
709 }
Michael Wachenschwanzc8c26362018-09-07 14:59:25 -0700710}