/**
* Copyright (C) 2015 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 android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.util.IndentingPrintWriter;
/**
* Keeps track of recent active state changes in apps.
* Access should be guarded by a lock by the caller.
*/
public class AppIdleHistory {
private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>();
private long lastPeriod = 0;
private static final long ONE_MINUTE = 60 * 1000;
private static final int HISTORY_SIZE = 100;
private static final int FLAG_LAST_STATE = 2;
private static final int FLAG_PARTIAL_ACTIVE = 1;
private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE
: 60 * ONE_MINUTE;
public void addEntry(String packageName, int userId, boolean idle, long timeNow) {
ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
byte[] packageHistory = getPackageHistory(userHistory, packageName);
long thisPeriod = timeNow / PERIOD_DURATION;
// Has the period switched over? Slide all users' package histories
if (lastPeriod != 0 && lastPeriod < thisPeriod
&& (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) {
int diff = (int) (thisPeriod - lastPeriod);
final int NUSERS = mIdleHistory.size();
for (int u = 0; u < NUSERS; u++) {
userHistory = mIdleHistory.valueAt(u);
for (byte[] history : userHistory.values()) {
// Shift left
System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff);
// Replicate last state across the diff
for (int i = 0; i < diff; i++) {
history[HISTORY_SIZE - i - 1] =
(byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE);
}
}
}
}
lastPeriod = thisPeriod;
if (!idle) {
packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
} else {
packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE;
}
}
private ArrayMap<String, byte[]> getUserHistory(int userId) {
ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
if (userHistory == null) {
userHistory = new ArrayMap<>();
mIdleHistory.put(userId, userHistory);
}
return userHistory;
}
private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) {
byte[] packageHistory = userHistory.get(packageName);
if (packageHistory == null) {
packageHistory = new byte[HISTORY_SIZE];
userHistory.put(packageName, packageHistory);
}
return packageHistory;
}
public void removeUser(int userId) {
mIdleHistory.remove(userId);
}
public boolean isIdle(int userId, String packageName) {
ArrayMap<String, byte[]> userHistory = getUserHistory(userId);
byte[] packageHistory = getPackageHistory(userHistory, packageName);
return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0;
}
public void dump(IndentingPrintWriter idpw, int userId) {
ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId);
if (userHistory == null) return;
final int P = userHistory.size();
for (int p = 0; p < P; p++) {
final String packageName = userHistory.keyAt(p);
final byte[] history = userHistory.valueAt(p);
for (int i = 0; i < HISTORY_SIZE; i++) {
idpw.print(history[i] == 0 ? '.' : 'A');
}
idpw.print(" " + packageName);
idpw.println();
}
}
}