1/** 2 * Copyright (C) 2015 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 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17package com.android.server.usage; 18 19import android.util.ArrayMap; 20import android.util.SparseArray; 21 22import com.android.internal.util.IndentingPrintWriter; 23 24/** 25 * Keeps track of recent active state changes in apps. 26 * Access should be guarded by a lock by the caller. 27 */ 28public class AppIdleHistory { 29 30 private SparseArray<ArrayMap<String,byte[]>> mIdleHistory = new SparseArray<>(); 31 private long lastPeriod = 0; 32 private static final long ONE_MINUTE = 60 * 1000; 33 private static final int HISTORY_SIZE = 100; 34 private static final int FLAG_LAST_STATE = 2; 35 private static final int FLAG_PARTIAL_ACTIVE = 1; 36 private static final long PERIOD_DURATION = UsageStatsService.COMPRESS_TIME ? ONE_MINUTE 37 : 60 * ONE_MINUTE; 38 39 public void addEntry(String packageName, int userId, boolean idle, long timeNow) { 40 ArrayMap<String, byte[]> userHistory = getUserHistory(userId); 41 byte[] packageHistory = getPackageHistory(userHistory, packageName); 42 43 long thisPeriod = timeNow / PERIOD_DURATION; 44 // Has the period switched over? Slide all users' package histories 45 if (lastPeriod != 0 && lastPeriod < thisPeriod 46 && (thisPeriod - lastPeriod) < HISTORY_SIZE - 1) { 47 int diff = (int) (thisPeriod - lastPeriod); 48 final int NUSERS = mIdleHistory.size(); 49 for (int u = 0; u < NUSERS; u++) { 50 userHistory = mIdleHistory.valueAt(u); 51 for (byte[] history : userHistory.values()) { 52 // Shift left 53 System.arraycopy(history, diff, history, 0, HISTORY_SIZE - diff); 54 // Replicate last state across the diff 55 for (int i = 0; i < diff; i++) { 56 history[HISTORY_SIZE - i - 1] = 57 (byte) (history[HISTORY_SIZE - diff - 1] & FLAG_LAST_STATE); 58 } 59 } 60 } 61 } 62 lastPeriod = thisPeriod; 63 if (!idle) { 64 packageHistory[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE; 65 } else { 66 packageHistory[HISTORY_SIZE - 1] &= ~FLAG_LAST_STATE; 67 } 68 } 69 70 private ArrayMap<String, byte[]> getUserHistory(int userId) { 71 ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId); 72 if (userHistory == null) { 73 userHistory = new ArrayMap<>(); 74 mIdleHistory.put(userId, userHistory); 75 } 76 return userHistory; 77 } 78 79 private byte[] getPackageHistory(ArrayMap<String, byte[]> userHistory, String packageName) { 80 byte[] packageHistory = userHistory.get(packageName); 81 if (packageHistory == null) { 82 packageHistory = new byte[HISTORY_SIZE]; 83 userHistory.put(packageName, packageHistory); 84 } 85 return packageHistory; 86 } 87 88 public void removeUser(int userId) { 89 mIdleHistory.remove(userId); 90 } 91 92 public boolean isIdle(int userId, String packageName) { 93 ArrayMap<String, byte[]> userHistory = getUserHistory(userId); 94 byte[] packageHistory = getPackageHistory(userHistory, packageName); 95 return (packageHistory[HISTORY_SIZE - 1] & FLAG_LAST_STATE) == 0; 96 } 97 98 public void dump(IndentingPrintWriter idpw, int userId) { 99 ArrayMap<String, byte[]> userHistory = mIdleHistory.get(userId); 100 if (userHistory == null) return; 101 final int P = userHistory.size(); 102 for (int p = 0; p < P; p++) { 103 final String packageName = userHistory.keyAt(p); 104 final byte[] history = userHistory.valueAt(p); 105 for (int i = 0; i < HISTORY_SIZE; i++) { 106 idpw.print(history[i] == 0 ? '.' : 'A'); 107 } 108 idpw.print(" " + packageName); 109 idpw.println(); 110 } 111 } 112}