Catch exceptions in UsageStatsService on bad data.
Instead of throwing exceptions from UsageStatsService and potentially crashing the system server, catch exception on reading/writing bad data and log the exception. Also ensure proper handling of deobfuscating bad data. Bug: 140459061 Bug: 135484470 Test: atest IntervalStatsTests Test: atest UsageStatsDatabaseTest Change-Id: I17d6ad5e377a2636ff42c9b422fe6ddf1201fc08
This commit is contained in:
@@ -78,6 +78,21 @@ public class EventList {
|
||||
mEvents.add(insertIndex, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the event at the given index.
|
||||
*
|
||||
* @param index the index of the event to remove
|
||||
* @return the event removed, or {@code null} if the index was out of bounds
|
||||
*/
|
||||
public UsageEvents.Event remove(int index) {
|
||||
try {
|
||||
return mEvents.remove(index);
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
// catch and handle the exception here instead of throwing it to the client
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index of the first event whose timestamp is greater than or equal to the given
|
||||
* timestamp.
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.usage;
|
||||
|
||||
import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.content.res.Configuration;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class IntervalStatsTests {
|
||||
private static final int NUMBER_OF_PACKAGES = 7;
|
||||
private static final int NUMBER_OF_EVENTS_PER_PACKAGE = 200;
|
||||
private static final int NUMBER_OF_EVENTS = NUMBER_OF_PACKAGES * NUMBER_OF_EVENTS_PER_PACKAGE;
|
||||
|
||||
private long mEndTime = 0;
|
||||
|
||||
private void populateIntervalStats(IntervalStats intervalStats) {
|
||||
final int timeProgression = 23;
|
||||
long time = System.currentTimeMillis() - (NUMBER_OF_EVENTS * timeProgression);
|
||||
|
||||
intervalStats.majorVersion = 7;
|
||||
intervalStats.minorVersion = 8;
|
||||
intervalStats.beginTime = time;
|
||||
intervalStats.interactiveTracker.count = 2;
|
||||
intervalStats.interactiveTracker.duration = 111111;
|
||||
intervalStats.nonInteractiveTracker.count = 3;
|
||||
intervalStats.nonInteractiveTracker.duration = 222222;
|
||||
intervalStats.keyguardShownTracker.count = 4;
|
||||
intervalStats.keyguardShownTracker.duration = 333333;
|
||||
intervalStats.keyguardHiddenTracker.count = 5;
|
||||
intervalStats.keyguardHiddenTracker.duration = 4444444;
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_EVENTS; i++) {
|
||||
UsageEvents.Event event = new UsageEvents.Event();
|
||||
final int packageInt = ((i / 3) % NUMBER_OF_PACKAGES); // clusters of 3 events
|
||||
event.mPackage = "fake.package.name" + packageInt;
|
||||
if (packageInt == 3) {
|
||||
// Third app is an instant app
|
||||
event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
|
||||
}
|
||||
|
||||
final int instanceId = i % 11;
|
||||
event.mClass = ".fake.class.name" + instanceId;
|
||||
event.mTimeStamp = time;
|
||||
event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
|
||||
event.mInstanceId = instanceId;
|
||||
|
||||
|
||||
final int rootPackageInt = (i % 5); // 5 "apps" start each task
|
||||
event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
|
||||
|
||||
final int rootClassInt = i % 6;
|
||||
event.mTaskRootClass = ".fake.class.name" + rootClassInt;
|
||||
|
||||
switch (event.mEventType) {
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE:
|
||||
event.mConfiguration = new Configuration(); //empty config
|
||||
break;
|
||||
case UsageEvents.Event.SHORTCUT_INVOCATION:
|
||||
event.mShortcutId = "shortcut" + (i % 8); //"random" shortcut
|
||||
break;
|
||||
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
|
||||
//"random" bucket and reason
|
||||
event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
|
||||
break;
|
||||
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
|
||||
event.mNotificationChannelId = "channel" + (i % 5); //"random" channel
|
||||
break;
|
||||
}
|
||||
|
||||
intervalStats.addEvent(event);
|
||||
intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
|
||||
event.mInstanceId);
|
||||
|
||||
time += timeProgression; // Arbitrary progression of time
|
||||
}
|
||||
mEndTime = time;
|
||||
|
||||
final Configuration config1 = new Configuration();
|
||||
config1.fontScale = 3.3f;
|
||||
config1.mcc = 4;
|
||||
intervalStats.getOrCreateConfigurationStats(config1);
|
||||
|
||||
final Configuration config2 = new Configuration();
|
||||
config2.mnc = 5;
|
||||
config2.setLocale(new Locale("en", "US"));
|
||||
intervalStats.getOrCreateConfigurationStats(config2);
|
||||
|
||||
intervalStats.activeConfiguration = config2;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObfuscation() {
|
||||
final IntervalStats intervalStats = new IntervalStats();
|
||||
populateIntervalStats(intervalStats);
|
||||
|
||||
final PackagesTokenData packagesTokenData = new PackagesTokenData();
|
||||
intervalStats.obfuscateData(packagesTokenData);
|
||||
|
||||
// data is populated with 7 different "apps"
|
||||
assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
|
||||
assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
|
||||
assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
|
||||
|
||||
assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
|
||||
assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeobfuscation() {
|
||||
final IntervalStats intervalStats = new IntervalStats();
|
||||
populateIntervalStats(intervalStats);
|
||||
|
||||
final PackagesTokenData packagesTokenData = new PackagesTokenData();
|
||||
intervalStats.obfuscateData(packagesTokenData);
|
||||
intervalStats.deobfuscateData(packagesTokenData);
|
||||
|
||||
// ensure deobfuscation doesn't update any of the mappings data
|
||||
assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
|
||||
assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
|
||||
assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
|
||||
|
||||
// ensure deobfuscation didn't remove any events or usage stats
|
||||
assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
|
||||
assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadDataOnDeobfuscation() {
|
||||
final IntervalStats intervalStats = new IntervalStats();
|
||||
populateIntervalStats(intervalStats);
|
||||
|
||||
final PackagesTokenData packagesTokenData = new PackagesTokenData();
|
||||
intervalStats.obfuscateData(packagesTokenData);
|
||||
intervalStats.packageStats.clear();
|
||||
|
||||
// remove the mapping for token 2
|
||||
packagesTokenData.tokensToPackagesMap.remove(2);
|
||||
|
||||
intervalStats.deobfuscateData(packagesTokenData);
|
||||
// deobfuscation should have removed all events mapped to package token 2
|
||||
assertEquals(intervalStats.events.size(),
|
||||
NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE - 1);
|
||||
assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES - 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadPackageDataOnDeobfuscation() {
|
||||
final IntervalStats intervalStats = new IntervalStats();
|
||||
populateIntervalStats(intervalStats);
|
||||
|
||||
final PackagesTokenData packagesTokenData = new PackagesTokenData();
|
||||
intervalStats.obfuscateData(packagesTokenData);
|
||||
intervalStats.packageStats.clear();
|
||||
|
||||
// remove mapping number 2 within package 3 (random)
|
||||
packagesTokenData.tokensToPackagesMap.valueAt(3).remove(2);
|
||||
|
||||
intervalStats.deobfuscateData(packagesTokenData);
|
||||
// deobfuscation should not have removed all events for a package - however, it's possible
|
||||
// that some events were removed because of how shortcut and notification events are handled
|
||||
assertTrue(intervalStats.events.size() > NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE);
|
||||
assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import android.content.res.Configuration;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.proto.ProtoInputStream;
|
||||
@@ -55,6 +56,8 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class IntervalStats {
|
||||
private static final String TAG = "IntervalStats";
|
||||
|
||||
public static final int CURRENT_MAJOR_VERSION = 1;
|
||||
public static final int CURRENT_MINOR_VERSION = 1;
|
||||
public int majorVersion = CURRENT_MAJOR_VERSION;
|
||||
@@ -453,6 +456,10 @@ public class IntervalStats {
|
||||
final UsageStats usageStats = packageStatsObfuscated.valueAt(statsIndex);
|
||||
usageStats.mPackageName = packagesTokenData.getString(packageToken,
|
||||
PackagesTokenData.PACKAGE_NAME_INDEX);
|
||||
if (usageStats.mPackageName == null) {
|
||||
Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update chooser counts
|
||||
final int chooserActionsSize = usageStats.mChooserCountsObfuscated.size();
|
||||
@@ -460,6 +467,11 @@ public class IntervalStats {
|
||||
final ArrayMap<String, Integer> categoryCountsMap = new ArrayMap<>();
|
||||
final int actionToken = usageStats.mChooserCountsObfuscated.keyAt(actionIndex);
|
||||
final String action = packagesTokenData.getString(packageToken, actionToken);
|
||||
if (action == null) {
|
||||
Slog.i(TAG, "Unable to parse chooser action " + actionToken
|
||||
+ " for package " + packageToken);
|
||||
continue;
|
||||
}
|
||||
final SparseIntArray categoryCounts =
|
||||
usageStats.mChooserCountsObfuscated.valueAt(actionIndex);
|
||||
final int categoriesSize = categoryCounts.size();
|
||||
@@ -467,6 +479,11 @@ public class IntervalStats {
|
||||
final int categoryToken = categoryCounts.keyAt(categoryIndex);
|
||||
final String category = packagesTokenData.getString(packageToken,
|
||||
categoryToken);
|
||||
if (category == null) {
|
||||
Slog.i(TAG, "Unable to parse chooser category " + categoryToken
|
||||
+ " for package " + packageToken);
|
||||
continue;
|
||||
}
|
||||
categoryCountsMap.put(category, categoryCounts.valueAt(categoryIndex));
|
||||
}
|
||||
usageStats.mChooserCounts.put(action, categoryCountsMap);
|
||||
@@ -481,22 +498,39 @@ public class IntervalStats {
|
||||
* shortcut or notification channel tokens.
|
||||
*/
|
||||
private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
|
||||
final int eventsSize = this.events.size();
|
||||
for (int i = 0; i < eventsSize; i++) {
|
||||
for (int i = this.events.size() - 1; i >= 0; i--) {
|
||||
final Event event = this.events.get(i);
|
||||
final int packageToken = event.mPackageToken;
|
||||
event.mPackage = packagesTokenData.getString(packageToken,
|
||||
PackagesTokenData.PACKAGE_NAME_INDEX);
|
||||
if (event.mPackage == null) {
|
||||
Slog.e(TAG, "Unable to parse event package " + packageToken);
|
||||
this.events.remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
event.mClass = packagesTokenData.getString(packageToken, event.mClassToken);
|
||||
if (event.mClass == null) {
|
||||
Slog.i(TAG, "Unable to parse class " + event.mClassToken
|
||||
+ " for package " + packageToken);
|
||||
}
|
||||
}
|
||||
if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
event.mTaskRootPackage = packagesTokenData.getString(packageToken,
|
||||
event.mTaskRootPackageToken);
|
||||
if (event.mTaskRootPackage == null) {
|
||||
Slog.i(TAG, "Unable to parse task root package " + event.mTaskRootPackageToken
|
||||
+ " for package " + packageToken);
|
||||
}
|
||||
}
|
||||
if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
event.mTaskRootClass = packagesTokenData.getString(packageToken,
|
||||
event.mTaskRootClassToken);
|
||||
if (event.mTaskRootClass == null) {
|
||||
Slog.i(TAG, "Unable to parse task root class " + event.mTaskRootClassToken
|
||||
+ " for package " + packageToken);
|
||||
}
|
||||
}
|
||||
switch (event.mEventType) {
|
||||
case CONFIGURATION_CHANGE:
|
||||
@@ -507,10 +541,23 @@ public class IntervalStats {
|
||||
case SHORTCUT_INVOCATION:
|
||||
event.mShortcutId = packagesTokenData.getString(packageToken,
|
||||
event.mShortcutIdToken);
|
||||
if (event.mShortcutId == null) {
|
||||
Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
|
||||
+ " for package " + packageToken);
|
||||
this.events.remove(i);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_INTERRUPTION:
|
||||
event.mNotificationChannelId = packagesTokenData.getString(packageToken,
|
||||
event.mNotificationChannelIdToken);
|
||||
if (event.mNotificationChannelId == null) {
|
||||
Slog.e(TAG, "Unable to parse notification channel "
|
||||
+ event.mNotificationChannelIdToken + " for package "
|
||||
+ packageToken);
|
||||
this.events.remove(i);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.server.usage;
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -107,9 +108,17 @@ public final class PackagesTokenData {
|
||||
*
|
||||
* @param packageToken the package token for which this token belongs to
|
||||
* @param token the token whose string needs to be fetched
|
||||
* @return the string representing the given token
|
||||
* @return the string representing the given token or {@code null} if not found
|
||||
*/
|
||||
public String getString(int packageToken, int token) {
|
||||
return tokensToPackagesMap.get(packageToken).get(token);
|
||||
try {
|
||||
return tokensToPackagesMap.get(packageToken).get(token);
|
||||
} catch (NullPointerException npe) {
|
||||
Slog.e("PackagesTokenData",
|
||||
"Unable to find tokenized strings for package " + packageToken, npe);
|
||||
return null;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,11 +869,19 @@ public class UsageStatsDatabase {
|
||||
UsageStatsXml.write(out, stats);
|
||||
break;
|
||||
case 4:
|
||||
UsageStatsProto.write(out, stats);
|
||||
try {
|
||||
UsageStatsProto.write(out, stats);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write interval stats to proto.", e);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
stats.obfuscateData(packagesTokenData);
|
||||
UsageStatsProtoV2.write(out, stats);
|
||||
try {
|
||||
UsageStatsProtoV2.write(out, stats);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write interval stats to proto.", e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException(
|
||||
@@ -920,10 +928,18 @@ public class UsageStatsDatabase {
|
||||
UsageStatsXml.read(in, statsOut);
|
||||
break;
|
||||
case 4:
|
||||
UsageStatsProto.read(in, statsOut);
|
||||
try {
|
||||
UsageStatsProto.read(in, statsOut);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read interval stats from proto.", e);
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
UsageStatsProtoV2.read(in, statsOut);
|
||||
try {
|
||||
UsageStatsProtoV2.read(in, statsOut);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read interval stats from proto.", e);
|
||||
}
|
||||
statsOut.deobfuscateData(packagesTokenData);
|
||||
break;
|
||||
default:
|
||||
@@ -974,6 +990,8 @@ public class UsageStatsDatabase {
|
||||
UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData);
|
||||
file.finishWrite(fos);
|
||||
fos = null;
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write obfuscated data to proto.", e);
|
||||
} finally {
|
||||
file.failWrite(fos);
|
||||
}
|
||||
|
||||
@@ -120,10 +120,14 @@ final class UsageStatsProto {
|
||||
IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
|
||||
break;
|
||||
case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
|
||||
final long chooserToken = proto.start(
|
||||
IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
|
||||
loadChooserCounts(proto, stats);
|
||||
proto.end(chooserToken);
|
||||
try {
|
||||
final long chooserToken = proto.start(
|
||||
IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
|
||||
loadChooserCounts(proto, stats);
|
||||
proto.end(chooserToken);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageName, e);
|
||||
}
|
||||
break;
|
||||
case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS:
|
||||
// Time attributes stored is an offset of the beginTime.
|
||||
@@ -153,20 +157,24 @@ final class UsageStatsProto {
|
||||
}
|
||||
|
||||
private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
|
||||
IntervalStats.EventTracker tracker) throws IOException {
|
||||
final long token = proto.start(fieldId);
|
||||
while (true) {
|
||||
switch (proto.nextField()) {
|
||||
case (int) IntervalStatsProto.CountAndTime.COUNT:
|
||||
tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
|
||||
break;
|
||||
case (int) IntervalStatsProto.CountAndTime.TIME_MS:
|
||||
tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
proto.end(token);
|
||||
return;
|
||||
IntervalStats.EventTracker tracker) {
|
||||
try {
|
||||
final long token = proto.start(fieldId);
|
||||
while (true) {
|
||||
switch (proto.nextField()) {
|
||||
case (int) IntervalStatsProto.CountAndTime.COUNT:
|
||||
tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
|
||||
break;
|
||||
case (int) IntervalStatsProto.CountAndTime.TIME_MS:
|
||||
tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
proto.end(token);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +314,7 @@ final class UsageStatsProto {
|
||||
}
|
||||
|
||||
private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
|
||||
throws IOException {
|
||||
throws IllegalArgumentException {
|
||||
final long token = proto.start(IntervalStatsProto.STRINGPOOL);
|
||||
final int size = stats.mStringCache.size();
|
||||
proto.write(IntervalStatsProto.StringPool.SIZE, size);
|
||||
@@ -317,7 +325,8 @@ final class UsageStatsProto {
|
||||
}
|
||||
|
||||
private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
|
||||
final IntervalStats stats, final UsageStats usageStats) throws IOException {
|
||||
final IntervalStats stats, final UsageStats usageStats)
|
||||
throws IllegalArgumentException {
|
||||
final long token = proto.start(fieldId);
|
||||
// Write the package name first, so loadUsageStats can avoid creating an extra object
|
||||
final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
|
||||
@@ -347,12 +356,16 @@ final class UsageStatsProto {
|
||||
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
|
||||
usageStats.mTotalTimeVisible);
|
||||
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
|
||||
writeChooserCounts(proto, usageStats);
|
||||
try {
|
||||
writeChooserCounts(proto, usageStats);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write chooser counts for " + usageStats.mPackageName, e);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
|
||||
long time) throws IOException {
|
||||
long time) throws IllegalArgumentException {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
|
||||
proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
|
||||
@@ -361,7 +374,7 @@ final class UsageStatsProto {
|
||||
|
||||
|
||||
private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
|
||||
throws IOException {
|
||||
throws IllegalArgumentException {
|
||||
if (usageStats == null || usageStats.mChooserCounts == null
|
||||
|| usageStats.mChooserCounts.keySet().isEmpty()) {
|
||||
return;
|
||||
@@ -381,7 +394,7 @@ final class UsageStatsProto {
|
||||
}
|
||||
|
||||
private static void writeCountsForAction(ProtoOutputStream proto,
|
||||
ArrayMap<String, Integer> counts) throws IOException {
|
||||
ArrayMap<String, Integer> counts) throws IllegalArgumentException {
|
||||
final int countsSize = counts.size();
|
||||
for (int i = 0; i < countsSize; i++) {
|
||||
String key = counts.keyAt(i);
|
||||
@@ -397,7 +410,7 @@ final class UsageStatsProto {
|
||||
|
||||
private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
|
||||
final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
|
||||
throws IOException {
|
||||
throws IllegalArgumentException {
|
||||
final long token = proto.start(fieldId);
|
||||
configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
|
||||
proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
|
||||
@@ -410,7 +423,7 @@ final class UsageStatsProto {
|
||||
}
|
||||
|
||||
private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
|
||||
final UsageEvents.Event event) throws IOException {
|
||||
final UsageEvents.Event event) throws IllegalArgumentException {
|
||||
final long token = proto.start(fieldId);
|
||||
final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
|
||||
if (packageIndex >= 0) {
|
||||
@@ -541,17 +554,33 @@ final class UsageStatsProto {
|
||||
statsOut.keyguardHiddenTracker);
|
||||
break;
|
||||
case (int) IntervalStatsProto.STRINGPOOL:
|
||||
stringPool = readStringPool(proto);
|
||||
statsOut.mStringCache.addAll(stringPool);
|
||||
try {
|
||||
stringPool = readStringPool(proto);
|
||||
statsOut.mStringCache.addAll(stringPool);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read string pool from proto.", e);
|
||||
}
|
||||
break;
|
||||
case (int) IntervalStatsProto.PACKAGES:
|
||||
loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
|
||||
try {
|
||||
loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some usage stats from proto.", e);
|
||||
}
|
||||
break;
|
||||
case (int) IntervalStatsProto.CONFIGURATIONS:
|
||||
loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
|
||||
try {
|
||||
loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
|
||||
}
|
||||
break;
|
||||
case (int) IntervalStatsProto.EVENT_LOG:
|
||||
loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
|
||||
try {
|
||||
loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some events from proto.", e);
|
||||
}
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
if (statsOut.endTime == 0) {
|
||||
@@ -570,37 +599,58 @@ final class UsageStatsProto {
|
||||
* @param proto The serializer to which to write the packageStats data.
|
||||
* @param stats The stats object to write to the XML file.
|
||||
*/
|
||||
public static void write(OutputStream out, IntervalStats stats) throws IOException {
|
||||
public static void write(OutputStream out, IntervalStats stats)
|
||||
throws IOException, IllegalArgumentException {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream(out);
|
||||
proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
|
||||
proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion);
|
||||
proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion);
|
||||
// String pool should be written before the rest of the usage stats
|
||||
writeStringPool(proto, stats);
|
||||
try {
|
||||
writeStringPool(proto, stats);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write string pool to proto.", e);
|
||||
}
|
||||
|
||||
writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
|
||||
stats.interactiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
|
||||
stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
|
||||
stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
|
||||
stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
|
||||
try {
|
||||
writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
|
||||
stats.interactiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
|
||||
stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
|
||||
stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
|
||||
stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
|
||||
}
|
||||
|
||||
final int statsCount = stats.packageStats.size();
|
||||
for (int i = 0; i < statsCount; i++) {
|
||||
writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
|
||||
stats.packageStats.valueAt(i));
|
||||
try {
|
||||
writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
|
||||
stats.packageStats.valueAt(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some usage stats to proto.", e);
|
||||
}
|
||||
}
|
||||
final int configCount = stats.configurations.size();
|
||||
for (int i = 0; i < configCount; i++) {
|
||||
boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
|
||||
writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
|
||||
stats.configurations.valueAt(i), active);
|
||||
try {
|
||||
writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
|
||||
stats.configurations.valueAt(i), active);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
|
||||
}
|
||||
}
|
||||
final int eventCount = stats.events.size();
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
|
||||
try {
|
||||
writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some events to proto.", e);
|
||||
}
|
||||
}
|
||||
|
||||
proto.flush();
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.app.usage.ConfigurationStats;
|
||||
import android.app.usage.UsageEvents;
|
||||
import android.app.usage.UsageStats;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.proto.ProtoInputStream;
|
||||
@@ -62,9 +63,13 @@ final class UsageStatsProtoV2 {
|
||||
UsageStatsObfuscatedProto.APP_LAUNCH_COUNT);
|
||||
break;
|
||||
case (int) UsageStatsObfuscatedProto.CHOOSER_ACTIONS:
|
||||
final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
|
||||
loadChooserCounts(proto, stats);
|
||||
proto.end(token);
|
||||
try {
|
||||
final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
|
||||
loadChooserCounts(proto, stats);
|
||||
proto.end(token);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageToken);
|
||||
}
|
||||
break;
|
||||
case (int) UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS:
|
||||
stats.mLastTimeForegroundServiceUsed = beginTime + proto.readLong(
|
||||
@@ -93,21 +98,26 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
|
||||
IntervalStats.EventTracker tracker) throws IOException {
|
||||
final long token = proto.start(fieldId);
|
||||
while (true) {
|
||||
switch (proto.nextField()) {
|
||||
case (int) IntervalStatsObfuscatedProto.CountAndTime.COUNT:
|
||||
tracker.count = proto.readInt(IntervalStatsObfuscatedProto.CountAndTime.COUNT);
|
||||
break;
|
||||
case (int) IntervalStatsObfuscatedProto.CountAndTime.TIME_MS:
|
||||
tracker.duration = proto.readLong(
|
||||
IntervalStatsObfuscatedProto.CountAndTime.TIME_MS);
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
proto.end(token);
|
||||
return;
|
||||
IntervalStats.EventTracker tracker) {
|
||||
try {
|
||||
final long token = proto.start(fieldId);
|
||||
while (true) {
|
||||
switch (proto.nextField()) {
|
||||
case (int) IntervalStatsObfuscatedProto.CountAndTime.COUNT:
|
||||
tracker.count = proto.readInt(
|
||||
IntervalStatsObfuscatedProto.CountAndTime.COUNT);
|
||||
break;
|
||||
case (int) IntervalStatsObfuscatedProto.CountAndTime.TIME_MS:
|
||||
tracker.duration = proto.readLong(
|
||||
IntervalStatsObfuscatedProto.CountAndTime.TIME_MS);
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
proto.end(token);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +288,7 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void writeUsageStats(ProtoOutputStream proto, final long beginTime,
|
||||
final UsageStats stats) throws IOException {
|
||||
final UsageStats stats) throws IllegalArgumentException {
|
||||
// Time attributes stored as an offset of the beginTime.
|
||||
proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1);
|
||||
proto.write(UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, stats.mLastTimeUsed - beginTime);
|
||||
@@ -291,11 +301,15 @@ final class UsageStatsProtoV2 {
|
||||
stats.mLastTimeVisible - beginTime);
|
||||
proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
|
||||
proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
|
||||
writeChooserCounts(proto, stats);
|
||||
try {
|
||||
writeChooserCounts(proto, stats);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write chooser counts for " + stats.mPackageName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
|
||||
long time) throws IOException {
|
||||
long time) throws IllegalArgumentException {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(IntervalStatsObfuscatedProto.CountAndTime.COUNT, count);
|
||||
proto.write(IntervalStatsObfuscatedProto.CountAndTime.TIME_MS, time);
|
||||
@@ -303,7 +317,7 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats stats)
|
||||
throws IOException {
|
||||
throws IllegalArgumentException {
|
||||
if (stats == null || stats.mChooserCountsObfuscated.size() == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -322,7 +336,7 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void writeCountsForAction(ProtoOutputStream proto, SparseIntArray counts)
|
||||
throws IOException {
|
||||
throws IllegalArgumentException {
|
||||
final int countsSize = counts.size();
|
||||
for (int i = 0; i < countsSize; i++) {
|
||||
final int category = counts.keyAt(i);
|
||||
@@ -339,7 +353,8 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void writeConfigStats(ProtoOutputStream proto, final long statsBeginTime,
|
||||
final ConfigurationStats configStats, boolean isActive) throws IOException {
|
||||
final ConfigurationStats configStats, boolean isActive)
|
||||
throws IllegalArgumentException {
|
||||
configStats.mConfiguration.writeToProto(proto,
|
||||
IntervalStatsObfuscatedProto.Configuration.CONFIG);
|
||||
proto.write(IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
|
||||
@@ -351,7 +366,7 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
|
||||
private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
|
||||
final UsageEvents.Event event) throws IOException {
|
||||
final UsageEvents.Event event) throws IllegalArgumentException {
|
||||
proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
|
||||
if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
|
||||
@@ -429,25 +444,39 @@ final class UsageStatsProtoV2 {
|
||||
stats.keyguardHiddenTracker);
|
||||
break;
|
||||
case (int) IntervalStatsObfuscatedProto.PACKAGES:
|
||||
final long packagesToken = proto.start(IntervalStatsObfuscatedProto.PACKAGES);
|
||||
UsageStats usageStats = parseUsageStats(proto, stats.beginTime);
|
||||
if (usageStats.mPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
stats.packageStatsObfuscated.put(usageStats.mPackageToken, usageStats);
|
||||
try {
|
||||
final long packagesToken = proto.start(
|
||||
IntervalStatsObfuscatedProto.PACKAGES);
|
||||
UsageStats usageStats = parseUsageStats(proto, stats.beginTime);
|
||||
if (usageStats.mPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
|
||||
stats.packageStatsObfuscated.put(usageStats.mPackageToken, usageStats);
|
||||
}
|
||||
proto.end(packagesToken);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some usage stats from proto.", e);
|
||||
}
|
||||
proto.end(packagesToken);
|
||||
break;
|
||||
case (int) IntervalStatsObfuscatedProto.CONFIGURATIONS:
|
||||
final long configsToken = proto.start(
|
||||
IntervalStatsObfuscatedProto.CONFIGURATIONS);
|
||||
loadConfigStats(proto, stats);
|
||||
proto.end(configsToken);
|
||||
try {
|
||||
final long configsToken = proto.start(
|
||||
IntervalStatsObfuscatedProto.CONFIGURATIONS);
|
||||
loadConfigStats(proto, stats);
|
||||
proto.end(configsToken);
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
|
||||
}
|
||||
break;
|
||||
case (int) IntervalStatsObfuscatedProto.EVENT_LOG:
|
||||
final long eventsToken = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG);
|
||||
UsageEvents.Event event = parseEvent(proto, stats.beginTime);
|
||||
proto.end(eventsToken);
|
||||
if (event != null) {
|
||||
stats.events.insert(event);
|
||||
try {
|
||||
final long eventsToken = proto.start(
|
||||
IntervalStatsObfuscatedProto.EVENT_LOG);
|
||||
UsageEvents.Event event = parseEvent(proto, stats.beginTime);
|
||||
proto.end(eventsToken);
|
||||
if (event != null) {
|
||||
stats.events.insert(event);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to read some events from proto.", e);
|
||||
}
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
@@ -466,39 +495,56 @@ final class UsageStatsProtoV2 {
|
||||
* @param out the output stream to which to write the interval stats data.
|
||||
* @param stats the interval stats object to write to the proto file.
|
||||
*/
|
||||
public static void write(OutputStream out, IntervalStats stats) throws IOException {
|
||||
public static void write(OutputStream out, IntervalStats stats)
|
||||
throws IOException, IllegalArgumentException {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream(out);
|
||||
proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, stats.endTime - stats.beginTime);
|
||||
proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion);
|
||||
proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion);
|
||||
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
|
||||
stats.interactiveTracker.count, stats.interactiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
|
||||
stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
|
||||
stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
|
||||
stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
|
||||
try {
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
|
||||
stats.interactiveTracker.count, stats.interactiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
|
||||
stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
|
||||
stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
|
||||
writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
|
||||
stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
|
||||
}
|
||||
|
||||
final int statsCount = stats.packageStatsObfuscated.size();
|
||||
for (int i = 0; i < statsCount; i++) {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGES);
|
||||
writeUsageStats(proto, stats.beginTime, stats.packageStatsObfuscated.valueAt(i));
|
||||
proto.end(token);
|
||||
try {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGES);
|
||||
writeUsageStats(proto, stats.beginTime, stats.packageStatsObfuscated.valueAt(i));
|
||||
proto.end(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some usage stats to proto.", e);
|
||||
}
|
||||
}
|
||||
final int configCount = stats.configurations.size();
|
||||
for (int i = 0; i < configCount; i++) {
|
||||
boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.CONFIGURATIONS);
|
||||
writeConfigStats(proto, stats.beginTime, stats.configurations.valueAt(i), active);
|
||||
proto.end(token);
|
||||
try {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.CONFIGURATIONS);
|
||||
writeConfigStats(proto, stats.beginTime, stats.configurations.valueAt(i), active);
|
||||
proto.end(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
|
||||
}
|
||||
}
|
||||
final int eventCount = stats.events.size();
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG);
|
||||
writeEvent(proto, stats.beginTime, stats.events.get(i));
|
||||
proto.end(token);
|
||||
try {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG);
|
||||
writeEvent(proto, stats.beginTime, stats.events.get(i));
|
||||
proto.end(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some events to proto.", e);
|
||||
}
|
||||
}
|
||||
|
||||
proto.flush();
|
||||
@@ -559,7 +605,7 @@ final class UsageStatsProtoV2 {
|
||||
* @param packagesTokenData the packages data object holding the data to write.
|
||||
*/
|
||||
static void writeObfuscatedData(OutputStream out, PackagesTokenData packagesTokenData)
|
||||
throws IOException {
|
||||
throws IOException, IllegalArgumentException {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream(out);
|
||||
proto.write(ObfuscatedPackagesProto.COUNTER, packagesTokenData.counter);
|
||||
|
||||
@@ -660,11 +706,15 @@ final class UsageStatsProtoV2 {
|
||||
while (true) {
|
||||
switch (proto.nextField()) {
|
||||
case (int) IntervalStatsObfuscatedProto.PENDING_EVENTS:
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
|
||||
UsageEvents.Event event = parsePendingEvent(proto);
|
||||
proto.end(token);
|
||||
if (event != null) {
|
||||
events.add(event);
|
||||
try {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
|
||||
UsageEvents.Event event = parsePendingEvent(proto);
|
||||
proto.end(token);
|
||||
if (event != null) {
|
||||
events.add(event);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Unable to parse some pending events from proto.", e);
|
||||
}
|
||||
break;
|
||||
case ProtoInputStream.NO_MORE_FIELDS:
|
||||
@@ -673,6 +723,47 @@ final class UsageStatsProtoV2 {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
|
||||
throws IllegalArgumentException {
|
||||
proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
|
||||
if (event.mClass != null) {
|
||||
proto.write(PendingEventProto.CLASS_NAME, event.mClass);
|
||||
}
|
||||
proto.write(PendingEventProto.TIME_MS, event.mTimeStamp);
|
||||
proto.write(PendingEventProto.FLAGS, event.mFlags);
|
||||
proto.write(PendingEventProto.TYPE, event.mEventType);
|
||||
proto.write(PendingEventProto.INSTANCE_ID, event.mInstanceId);
|
||||
if (event.mTaskRootPackage != null) {
|
||||
proto.write(PendingEventProto.TASK_ROOT_PACKAGE, event.mTaskRootPackage);
|
||||
}
|
||||
if (event.mTaskRootClass != null) {
|
||||
proto.write(PendingEventProto.TASK_ROOT_CLASS, event.mTaskRootClass);
|
||||
}
|
||||
switch (event.mEventType) {
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE:
|
||||
if (event.mConfiguration != null) {
|
||||
event.mConfiguration.writeToProto(proto, PendingEventProto.CONFIG);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
|
||||
if (event.mBucketAndReason != 0) {
|
||||
proto.write(PendingEventProto.STANDBY_BUCKET, event.mBucketAndReason);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.SHORTCUT_INVOCATION:
|
||||
if (event.mShortcutId != null) {
|
||||
proto.write(PendingEventProto.SHORTCUT_ID, event.mShortcutId);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
|
||||
if (event.mNotificationChannelId != null) {
|
||||
proto.write(PendingEventProto.NOTIFICATION_CHANNEL_ID,
|
||||
event.mNotificationChannelId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the pending events to a ProtoBuf file.
|
||||
*
|
||||
@@ -680,50 +771,17 @@ final class UsageStatsProtoV2 {
|
||||
* @param events the list of pending events.
|
||||
*/
|
||||
static void writePendingEvents(OutputStream out, LinkedList<UsageEvents.Event> events)
|
||||
throws IOException {
|
||||
throws IOException, IllegalArgumentException {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream(out);
|
||||
final int eventCount = events.size();
|
||||
for (int i = 0; i < eventCount; i++) {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
|
||||
final UsageEvents.Event event = events.get(i);
|
||||
proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
|
||||
if (event.mClass != null) {
|
||||
proto.write(PendingEventProto.CLASS_NAME, event.mClass);
|
||||
try {
|
||||
final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
|
||||
writePendingEvent(proto, events.get(i));
|
||||
proto.end(token);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Unable to write some pending events to proto.", e);
|
||||
}
|
||||
proto.write(PendingEventProto.TIME_MS, event.mTimeStamp);
|
||||
proto.write(PendingEventProto.FLAGS, event.mFlags);
|
||||
proto.write(PendingEventProto.TYPE, event.mEventType);
|
||||
proto.write(PendingEventProto.INSTANCE_ID, event.mInstanceId);
|
||||
if (event.mTaskRootPackage != null) {
|
||||
proto.write(PendingEventProto.TASK_ROOT_PACKAGE, event.mTaskRootPackage);
|
||||
}
|
||||
if (event.mTaskRootClass != null) {
|
||||
proto.write(PendingEventProto.TASK_ROOT_CLASS, event.mTaskRootClass);
|
||||
}
|
||||
switch (event.mEventType) {
|
||||
case UsageEvents.Event.CONFIGURATION_CHANGE:
|
||||
if (event.mConfiguration != null) {
|
||||
event.mConfiguration.writeToProto(proto, PendingEventProto.CONFIG);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
|
||||
if (event.mBucketAndReason != 0) {
|
||||
proto.write(PendingEventProto.STANDBY_BUCKET, event.mBucketAndReason);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.SHORTCUT_INVOCATION:
|
||||
if (event.mShortcutId != null) {
|
||||
proto.write(PendingEventProto.SHORTCUT_ID, event.mShortcutId);
|
||||
}
|
||||
break;
|
||||
case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
|
||||
if (event.mNotificationChannelId != null) {
|
||||
proto.write(PendingEventProto.NOTIFICATION_CHANNEL_ID,
|
||||
event.mNotificationChannelId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
proto.flush();
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ public class UsageStatsService extends SystemService implements
|
||||
af.finishWrite(fos);
|
||||
fos = null;
|
||||
pendingEvents.clear();
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath()
|
||||
+ " for user " + userId);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user