/** * 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(); } } }