• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 
17 package com.android.storagemanager.deletionhelper;
18 
19 import android.app.Activity;
20 import android.app.Application;
21 import android.os.Bundle;
22 import android.util.ArraySet;
23 import android.util.Log;
24 import com.android.internal.logging.MetricsLogger;
25 import com.android.internal.logging.MetricsProto.MetricsEvent;
26 import com.android.settingslib.applications.ApplicationsState;
27 
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
31 
32 /**
33  * AppDeletionType provides a list of apps which have not been used for a while on the system.
34  * It also provides the functionality to clear out these apps.
35  */
36 public class AppDeletionType implements DeletionType, ApplicationsState.Callbacks,
37         AppStateBaseBridge.Callback {
38     public static final String EXTRA_CHECKED_SET = "checkedSet";
39     private static final String TAG = "AppDeletionType";
40 
41     private FreeableChangedListener mListener;
42     private AppListener mAppListener;
43     private List<ApplicationsState.AppEntry> mAppEntries;
44     private ApplicationsState mState;
45     private ApplicationsState.Session mSession;
46     private HashSet<String> mCheckedApplications;
47     private AppStateUsageStatsBridge mDataUsageBridge;
48 
AppDeletionType(Application app, HashSet<String> checkedApplications)49     public AppDeletionType(Application app, HashSet<String> checkedApplications) {
50         mState = ApplicationsState.getInstance(app);
51         mSession = mState.newSession(this);
52         if (checkedApplications != null) {
53             mCheckedApplications = checkedApplications;
54         } else {
55             mCheckedApplications = new HashSet<>();
56         }
57         mDataUsageBridge = new AppStateUsageStatsBridge(app.getApplicationContext(), mState, this);
58     }
59 
60     @Override
registerFreeableChangedListener(FreeableChangedListener listener)61     public void registerFreeableChangedListener(FreeableChangedListener listener) {
62         mListener = listener;
63     }
64 
65     @Override
onResume()66     public void onResume() {
67         mSession.resume();
68         mDataUsageBridge.resume();
69     }
70 
71     @Override
onPause()72     public void onPause() {
73         mSession.pause();
74         mDataUsageBridge.pause();
75     }
76 
77     @Override
onSaveInstanceStateBundle(Bundle savedInstanceState)78     public void onSaveInstanceStateBundle(Bundle savedInstanceState) {
79         savedInstanceState.putSerializable(EXTRA_CHECKED_SET, mCheckedApplications);
80     }
81 
82     @Override
clearFreeableData(Activity activity)83     public void clearFreeableData(Activity activity) {
84         ArraySet<String> apps = new ArraySet<>();
85         for (ApplicationsState.AppEntry entry : mAppEntries) {
86             final String packageName;
87             synchronized (entry) {
88                 packageName = entry.info.packageName;
89             }
90             if (mCheckedApplications.contains(packageName)) {
91                 apps.add(packageName);
92             }
93         }
94         // TODO: If needed, add an action on the callback.
95         PackageDeletionTask task = new PackageDeletionTask(activity.getPackageManager(), apps,
96                 new PackageDeletionTask.Callback() {
97                     @Override
98                     public void onSuccess() {
99                     }
100 
101                     @Override
102                     public void onError() {
103                         Log.e(TAG, "An error occurred while uninstalling packages.");
104                         MetricsLogger.action(activity,
105                                 MetricsEvent.ACTION_DELETION_HELPER_APPS_DELETION_FAIL);
106                     }
107                 });
108 
109         task.run();
110     }
111 
112     @Override
onRunningStateChanged(boolean running)113     public void onRunningStateChanged(boolean running) {
114 
115     }
116 
117     @Override
onPackageListChanged()118     public void onPackageListChanged() {
119     }
120 
121     @Override
onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps)122     public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
123         if (apps == null) {
124             return;
125         }
126         mAppEntries = apps;
127         for (ApplicationsState.AppEntry app : mAppEntries) {
128             synchronized (app) {
129                 mState.ensureIcon(app);
130             }
131         }
132         if (mAppListener != null) {
133             mAppListener.onAppRebuild(mAppEntries);
134         }
135         maybeNotifyListener();
136     }
137 
138     @Override
onPackageIconChanged()139     public void onPackageIconChanged() {
140     }
141 
142     @Override
onPackageSizeChanged(String packageName)143     public void onPackageSizeChanged(String packageName) {
144         rebuild();
145     }
146 
147     @Override
onAllSizesComputed()148     public void onAllSizesComputed() {
149         rebuild();
150     }
151 
152     @Override
onLauncherInfoChanged()153     public void onLauncherInfoChanged() {
154 
155     }
156 
157     @Override
onLoadEntriesCompleted()158     public void onLoadEntriesCompleted() {
159         rebuild();
160     }
161 
162     @Override
onExtraInfoUpdated()163     public void onExtraInfoUpdated() {
164         rebuild();
165     }
166 
167     /**
168      * Registers a preference group view to notify when the app list changes.
169      */
registerView(AppDeletionPreferenceGroup preference)170     public void registerView(AppDeletionPreferenceGroup preference) {
171         mAppListener = preference;
172     }
173 
174     /**
175      * Set a package to be checked for deletion, if the apps are cleared.
176      * @param packageName The name of the package to potentially delete.
177      * @param isChecked Whether or not the package should be deleted.
178      */
setChecked(String packageName, boolean isChecked)179     public void setChecked(String packageName, boolean isChecked) {
180         if (isChecked) {
181             mCheckedApplications.add(packageName);
182         } else {
183             mCheckedApplications.remove(packageName);
184         }
185         maybeNotifyListener();
186     }
187 
188     /**
189      * Returns an amount of clearable app data.
190      * @param countUnchecked If unchecked applications should be counted for size purposes.
191      */
getTotalAppsFreeableSpace(boolean countUnchecked)192     public long getTotalAppsFreeableSpace(boolean countUnchecked) {
193         long freeableSpace = 0;
194         if (mAppEntries != null) {
195             for (int i = 0; i < mAppEntries.size(); i++) {
196                 final ApplicationsState.AppEntry entry = mAppEntries.get(i);
197                 long entrySize = entry.size;
198                 final String packageName;
199                 synchronized (entry) {
200                     packageName = entry.info.packageName;
201                 }
202                 // If the entrySize is negative, it is either an unknown size or an error occurred.
203                 if ((countUnchecked ||
204                         mCheckedApplications.contains(packageName)) && entrySize > 0) {
205                     freeableSpace += entrySize;
206                 }
207             }
208         }
209 
210         return freeableSpace;
211     }
212 
213     /**
214      * Returns a number of eligible, clearable apps.
215      */
getEligibleApps()216     public int getEligibleApps() {
217         if (mAppEntries == null) {
218             return 0;
219         }
220         return mAppEntries.size();
221     }
222 
223     /**
224      * Returns if a given package is slated for deletion.
225      * @param packageName The name of the package to check.
226      */
isChecked(String packageName)227     public boolean isChecked(String packageName) {
228         return mCheckedApplications.contains(packageName);
229     }
230 
rebuild()231     private void rebuild() {
232         mSession.rebuild(AppStateUsageStatsBridge.FILTER_USAGE_STATS,
233                 ApplicationsState.SIZE_COMPARATOR);
234     }
235 
maybeNotifyListener()236     private void maybeNotifyListener() {
237         if (mListener != null) {
238             mListener.onFreeableChanged(mAppEntries.size(), getTotalAppsFreeableSpace(true));
239         }
240     }
241 
242     /**
243      * An interface for listening for when the app list has been rebuilt.
244      */
245     public interface AppListener {
246         /**
247          * Callback to be called once the app list is rebuilt.
248          * @param apps A list of eligible, clearable AppEntries.
249          */
onAppRebuild(List<ApplicationsState.AppEntry> apps)250         void onAppRebuild(List<ApplicationsState.AppEntry> apps);
251     }
252 }
253