AppStateUsageStatsBridge.java revision cd65f0119e6ef2e282e19b200c07ae42ff9efe5f
1/* 2 * Copyright (C) 2016 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 */ 16package com.android.storagemanager.deletionhelper; 17 18import android.app.usage.UsageStats; 19import android.app.usage.UsageStatsManager; 20import android.content.Context; 21import android.content.pm.ApplicationInfo; 22 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageManager; 25import android.os.SystemProperties; 26import android.os.UserHandle; 27import android.util.Log; 28import com.android.storagemanager.deletionhelper.AppStateBaseBridge; 29import com.android.storagemanager.deletionhelper.AppStateBaseBridge.Callback; 30import com.android.settingslib.applications.ApplicationsState; 31import com.android.settingslib.applications.ApplicationsState.AppEntry; 32import com.android.settingslib.applications.ApplicationsState.AppFilter; 33 34import java.util.ArrayList; 35import java.util.Map; 36import java.util.concurrent.TimeUnit; 37 38/** 39 * Connects data from the UsageStatsManager to the ApplicationsState. 40 */ 41public class AppStateUsageStatsBridge extends AppStateBaseBridge { 42 private static final String TAG = "AppStateUsageStatsBridge"; 43 44 private static final String DEBUG_APP_UNUSED_OVERRIDE = "debug.asm.app_unused_limit"; 45 46 private UsageStatsManager mUsageStatsManager; 47 private PackageManager mPm; 48 public static final long NEVER_USED = -1; 49 public static final long UNKNOWN_LAST_USE = -2; 50 public static final long UNUSED_DAYS_DELETION_THRESHOLD = 90; 51 52 public AppStateUsageStatsBridge(Context context, ApplicationsState appState, 53 Callback callback) { 54 super(appState, callback); 55 mUsageStatsManager = 56 (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); 57 mPm = context.getPackageManager(); 58 } 59 60 @Override 61 protected void loadAllExtraInfo() { 62 ArrayList<AppEntry> apps = mAppSession.getAllApps(); 63 if (apps == null) return; 64 65 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats(0, 66 System.currentTimeMillis()); 67 for (AppEntry entry : apps) { 68 UsageStats usageStats = map.get(entry.info.packageName); 69 entry.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats), 70 getDaysSinceInstalled(entry.info.packageName), 71 UserHandle.getUserId(entry.info.uid)); 72 } 73 } 74 75 @Override 76 protected void updateExtraInfo(AppEntry app, String pkg, int uid) { 77 Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats(0, 78 System.currentTimeMillis()); 79 UsageStats usageStats = map.get(app.info.packageName); 80 app.extraInfo = new UsageStatsState(getDaysSinceLastUse(usageStats), 81 getDaysSinceInstalled(app.info.packageName), 82 UserHandle.getUserId(app.info.uid)); 83 } 84 85 private long getDaysSinceLastUse(UsageStats stats) { 86 if (stats == null) { 87 return NEVER_USED; 88 } 89 long lastUsed = stats.getLastTimeUsed(); 90 // Sometimes, a usage is recorded without a time and we don't know when the use was. 91 if (lastUsed == 0) { 92 return UNKNOWN_LAST_USE; 93 } 94 return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - lastUsed); 95 } 96 97 private long getDaysSinceInstalled(String packageName) { 98 PackageInfo pi = null; 99 try { 100 pi = mPm.getPackageInfo(packageName, 0); 101 } catch (PackageManager.NameNotFoundException e) { 102 Log.e(TAG, packageName + " was not found."); 103 } 104 105 if (pi == null) { 106 return NEVER_USED; 107 } 108 109 return (TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - pi.firstInstallTime)); 110 } 111 112 /** 113 * Filters only non-system apps which haven't been used in the last 60 days. If an app's last 114 * usage is unknown, it is skipped. 115 */ 116 public static final AppFilter FILTER_USAGE_STATS = new AppFilter() { 117 private long mUnusedDaysThreshold; 118 119 @Override 120 public void init() { 121 mUnusedDaysThreshold = SystemProperties.getLong(DEBUG_APP_UNUSED_OVERRIDE, 122 UNUSED_DAYS_DELETION_THRESHOLD); 123 } 124 125 @Override 126 public boolean filterApp(AppEntry info) { 127 if (info == null) return false; 128 return isExtraInfoValid(info.extraInfo) && !isBundled(info) 129 && !isPersistentProcess(info); 130 } 131 132 private boolean isExtraInfoValid(Object extraInfo) { 133 if (extraInfo == null || !(extraInfo instanceof UsageStatsState)) { 134 return false; 135 } 136 137 UsageStatsState state = (UsageStatsState) extraInfo; 138 long mostRecentUse = Math.max(state.daysSinceFirstInstall, state.daysSinceLastUse); 139 return mostRecentUse >= mUnusedDaysThreshold || mostRecentUse == NEVER_USED; 140 } 141 142 private boolean isBundled(AppEntry info) { 143 return (info.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 144 } 145 146 private boolean isPersistentProcess(AppEntry info) { 147 return (info.info.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; 148 } 149 }; 150 151 /** 152 * UsageStatsState contains the days since the last use and first install of a given app. 153 */ 154 public static class UsageStatsState { 155 public long daysSinceLastUse; 156 public long daysSinceFirstInstall; 157 public int userId; 158 159 public UsageStatsState(long daysSinceLastUse, long daysSinceFirstInstall, int userId) { 160 this.daysSinceLastUse = daysSinceLastUse; 161 this.daysSinceFirstInstall = daysSinceFirstInstall; 162 this.userId = userId; 163 } 164 } 165} 166