• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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  */
16 
17 package com.android.car.settings.applications;
18 
19 import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
20 
21 import android.app.usage.UsageStats;
22 import android.app.usage.UsageStatsManager;
23 import android.apphibernation.AppHibernationManager;
24 import android.content.Context;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.provider.DeviceConfig;
28 import android.util.ArrayMap;
29 
30 import androidx.annotation.NonNull;
31 
32 import com.android.settingslib.utils.ThreadUtils;
33 
34 import java.util.List;
35 import java.util.Map;
36 import java.util.concurrent.TimeUnit;
37 
38 /**
39  * Class for fetching and returning the number of hibernated apps. Largely derived from
40  * {@link com.android.settings.applications.HibernatedAppsPreferenceController}.
41  */
42 public class HibernatedAppsItemManager {
43     private static final String PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS =
44             "auto_revoke_unused_threshold_millis2";
45     private static final long DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90);
46 
47     private final Context mContext;
48 
49     private HibernatedAppsCountListener mListener;
50 
HibernatedAppsItemManager(Context context)51     public HibernatedAppsItemManager(Context context) {
52         mContext = context;
53     }
54 
55     /**
56      * Starts fetching recently used apps
57      */
startLoading()58     public void startLoading() {
59         ThreadUtils.postOnBackgroundThread(() -> {
60             int count = getNumHibernated();
61             ThreadUtils.postOnMainThread(() -> {
62                 HibernatedAppsCountListener localListener = mListener;
63                 if (localListener != null) {
64                     localListener.onHibernatedAppsCountLoaded(count);
65                 }
66             });
67         });
68     }
69 
70     /**
71      * Registers a listener that will be notified once the data is loaded.
72      */
setListener(@onNull HibernatedAppsCountListener listener)73     public void setListener(@NonNull HibernatedAppsCountListener listener) {
74         mListener = listener;
75     }
76 
getNumHibernated()77     private int getNumHibernated() {
78         // TODO(b/187465752): Find a way to export this logic from PermissionController module
79         PackageManager pm = mContext.getPackageManager();
80         AppHibernationManager ahm =
81                 mContext.getSystemService(AppHibernationManager.class);
82         List<String> hibernatedPackages = ahm.getHibernatingPackagesForUser();
83         int numHibernated = hibernatedPackages.size();
84 
85         // Also need to count packages that are auto revoked but not hibernated.
86         int numAutoRevoked = 0;
87         UsageStatsManager usm = mContext.getSystemService(UsageStatsManager.class);
88         long now = System.currentTimeMillis();
89         long unusedThreshold = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
90                 PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS, DEFAULT_UNUSED_THRESHOLD_MS);
91         List<UsageStats> usageStatsList = usm.queryUsageStats(INTERVAL_MONTHLY,
92                 now - unusedThreshold, now);
93         Map<String, UsageStats> recentlyUsedPackages = new ArrayMap<>();
94         for (UsageStats us : usageStatsList) {
95             recentlyUsedPackages.put(us.mPackageName, us);
96         }
97         List<PackageInfo> packages = pm.getInstalledPackages(
98                 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_PERMISSIONS);
99         for (PackageInfo pi : packages) {
100             String packageName = pi.packageName;
101             UsageStats usageStats = recentlyUsedPackages.get(packageName);
102             // Only count packages that have not been used recently as auto-revoked permissions may
103             // stay revoked even after use if the user has not regranted them.
104             boolean usedRecently = (usageStats != null
105                     && (now - usageStats.getLastTimeAnyComponentUsed() < unusedThreshold
106                     || now - usageStats.getLastTimeVisible() < unusedThreshold));
107             if (!hibernatedPackages.contains(packageName)
108                     && pi.requestedPermissions != null
109                     && !usedRecently) {
110                 for (String perm : pi.requestedPermissions) {
111                     if ((pm.getPermissionFlags(perm, packageName, mContext.getUser())
112                             & PackageManager.FLAG_PERMISSION_AUTO_REVOKED) != 0) {
113                         numAutoRevoked++;
114                         break;
115                     }
116                 }
117             }
118         }
119         return numHibernated + numAutoRevoked;
120     }
121 
122 
123     /**
124      * Callback that is called once the count of hibernated apps has been fetched.
125      */
126     public interface HibernatedAppsCountListener {
127         /**
128          * Called when the count of hibernated apps has loaded.
129          */
onHibernatedAppsCountLoaded(int hibernatedAppsCount)130         void onHibernatedAppsCountLoaded(int hibernatedAppsCount);
131     }
132 }
133