1 /* 2 * Copyright (C) 2019 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 package com.android.server.usage; 17 18 import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE; 19 20 import static junit.framework.Assert.assertEquals; 21 import static junit.framework.Assert.assertTrue; 22 import static junit.framework.Assert.fail; 23 24 import android.app.usage.UsageEvents; 25 import android.content.res.Configuration; 26 import android.test.suitebuilder.annotation.SmallTest; 27 28 import androidx.test.runner.AndroidJUnit4; 29 30 import com.android.internal.util.ArrayUtils; 31 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 35 import java.lang.reflect.Field; 36 import java.util.Locale; 37 38 @RunWith(AndroidJUnit4.class) 39 @SmallTest 40 public class IntervalStatsTests { 41 private static final int NUMBER_OF_PACKAGES = 7; 42 private static final int NUMBER_OF_EVENTS_PER_PACKAGE = 200; 43 private static final int NUMBER_OF_EVENTS = NUMBER_OF_PACKAGES * NUMBER_OF_EVENTS_PER_PACKAGE; 44 45 private long mEndTime = 0; 46 populateIntervalStats(IntervalStats intervalStats)47 private void populateIntervalStats(IntervalStats intervalStats) { 48 final int timeProgression = 23; 49 long time = System.currentTimeMillis() - (NUMBER_OF_EVENTS * timeProgression); 50 51 intervalStats.majorVersion = 7; 52 intervalStats.minorVersion = 8; 53 intervalStats.beginTime = time; 54 intervalStats.interactiveTracker.count = 2; 55 intervalStats.interactiveTracker.duration = 111111; 56 intervalStats.nonInteractiveTracker.count = 3; 57 intervalStats.nonInteractiveTracker.duration = 222222; 58 intervalStats.keyguardShownTracker.count = 4; 59 intervalStats.keyguardShownTracker.duration = 333333; 60 intervalStats.keyguardHiddenTracker.count = 5; 61 intervalStats.keyguardHiddenTracker.duration = 4444444; 62 63 for (int i = 0; i < NUMBER_OF_EVENTS; i++) { 64 UsageEvents.Event event = new UsageEvents.Event(); 65 final int packageInt = ((i / 3) % NUMBER_OF_PACKAGES); // clusters of 3 events 66 event.mPackage = "fake.package.name" + packageInt; 67 if (packageInt == 3) { 68 // Third app is an instant app 69 event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP; 70 } 71 72 final int instanceId = i % 11; 73 event.mClass = ".fake.class.name" + instanceId; 74 event.mTimeStamp = time; 75 event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type 76 event.mInstanceId = instanceId; 77 78 79 final int rootPackageInt = (i % 5); // 5 "apps" start each task 80 event.mTaskRootPackage = "fake.package.name" + rootPackageInt; 81 82 final int rootClassInt = i % 6; 83 event.mTaskRootClass = ".fake.class.name" + rootClassInt; 84 85 switch (event.mEventType) { 86 case UsageEvents.Event.CONFIGURATION_CHANGE: 87 event.mConfiguration = new Configuration(); //empty config 88 break; 89 case UsageEvents.Event.SHORTCUT_INVOCATION: 90 event.mShortcutId = "shortcut" + (i % 8); //"random" shortcut 91 break; 92 case UsageEvents.Event.STANDBY_BUCKET_CHANGED: 93 //"random" bucket and reason 94 event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8; 95 break; 96 case UsageEvents.Event.NOTIFICATION_INTERRUPTION: 97 event.mNotificationChannelId = "channel" + (i % 5); //"random" channel 98 break; 99 case UsageEvents.Event.LOCUS_ID_SET: 100 event.mLocusId = "locus" + (i % 7); //"random" locus 101 break; 102 } 103 104 intervalStats.addEvent(event); 105 intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType, 106 event.mInstanceId); 107 108 time += timeProgression; // Arbitrary progression of time 109 } 110 mEndTime = time; 111 112 final Configuration config1 = new Configuration(); 113 config1.fontScale = 3.3f; 114 config1.mcc = 4; 115 intervalStats.getOrCreateConfigurationStats(config1); 116 117 final Configuration config2 = new Configuration(); 118 config2.mnc = 5; 119 config2.setLocale(new Locale("en", "US")); 120 intervalStats.getOrCreateConfigurationStats(config2); 121 122 intervalStats.activeConfiguration = config2; 123 } 124 125 @Test testObfuscation()126 public void testObfuscation() { 127 final IntervalStats intervalStats = new IntervalStats(); 128 populateIntervalStats(intervalStats); 129 130 final PackagesTokenData packagesTokenData = new PackagesTokenData(); 131 intervalStats.obfuscateData(packagesTokenData); 132 133 // data is populated with 7 different "apps" 134 assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES); 135 assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES); 136 assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1); 137 138 assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS); 139 assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES); 140 } 141 142 @Test testDeobfuscation()143 public void testDeobfuscation() { 144 final IntervalStats intervalStats = new IntervalStats(); 145 populateIntervalStats(intervalStats); 146 147 final PackagesTokenData packagesTokenData = new PackagesTokenData(); 148 intervalStats.obfuscateData(packagesTokenData); 149 intervalStats.deobfuscateData(packagesTokenData); 150 151 // ensure deobfuscation doesn't update any of the mappings data 152 assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES); 153 assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES); 154 assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1); 155 156 // ensure deobfuscation didn't remove any events or usage stats 157 assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS); 158 assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES); 159 } 160 161 @Test testBadDataOnDeobfuscation()162 public void testBadDataOnDeobfuscation() { 163 final IntervalStats intervalStats = new IntervalStats(); 164 populateIntervalStats(intervalStats); 165 166 final PackagesTokenData packagesTokenData = new PackagesTokenData(); 167 intervalStats.obfuscateData(packagesTokenData); 168 intervalStats.packageStats.clear(); 169 170 // remove the mapping for token 2 171 packagesTokenData.tokensToPackagesMap.remove(2); 172 173 intervalStats.deobfuscateData(packagesTokenData); 174 // deobfuscation should have removed all events mapped to package token 2 175 assertEquals(intervalStats.events.size(), 176 NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE - 1); 177 assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES - 1); 178 } 179 180 @Test testBadPackageDataOnDeobfuscation()181 public void testBadPackageDataOnDeobfuscation() { 182 final IntervalStats intervalStats = new IntervalStats(); 183 populateIntervalStats(intervalStats); 184 185 final PackagesTokenData packagesTokenData = new PackagesTokenData(); 186 intervalStats.obfuscateData(packagesTokenData); 187 intervalStats.packageStats.clear(); 188 189 // remove mapping number 2 within package 3 (random) 190 packagesTokenData.tokensToPackagesMap.valueAt(3).remove(2); 191 192 intervalStats.deobfuscateData(packagesTokenData); 193 // deobfuscation should not have removed all events for a package - however, it's possible 194 // that some events were removed because of how shortcut and notification events are handled 195 assertTrue(intervalStats.events.size() > NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE); 196 assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES); 197 } 198 199 // All fields in this list are defined in IntervalStats and persisted - please ensure they're 200 // defined correctly in both usagestatsservice.proto and usagestatsservice_v2.proto 201 private static final String[] INTERVALSTATS_PERSISTED_FIELDS = {"beginTime", "endTime", 202 "mStringCache", "majorVersion", "minorVersion", "interactiveTracker", 203 "nonInteractiveTracker", "keyguardShownTracker", "keyguardHiddenTracker", 204 "packageStats", "configurations", "activeConfiguration", "events"}; 205 // All fields in this list are defined in IntervalStats but not persisted 206 private static final String[] INTERVALSTATS_IGNORED_FIELDS = {"lastTimeSaved", 207 "packageStatsObfuscated", "CURRENT_MAJOR_VERSION", "CURRENT_MINOR_VERSION", "TAG"}; 208 209 @Test testIntervalStatsFieldsAreKnown()210 public void testIntervalStatsFieldsAreKnown() { 211 final IntervalStats stats = new IntervalStats(); 212 final Field[] fields = stats.getClass().getDeclaredFields(); 213 for (Field field : fields) { 214 if (!(ArrayUtils.contains(INTERVALSTATS_PERSISTED_FIELDS, field.getName()) 215 || ArrayUtils.contains(INTERVALSTATS_IGNORED_FIELDS, field.getName()))) { 216 fail("Found an unknown field: " + field.getName() + ". Please correctly update " 217 + "either INTERVALSTATS_PERSISTED_FIELDS or INTERVALSTATS_IGNORED_FIELDS."); 218 } 219 } 220 } 221 } 222