import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.Formatter;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import java.io.File;
+import java.io.IOException;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
import java.util.regex.Pattern;
/**
final Context mContext;
final PackageManager mPm;
+ final IconDrawableFactory mDrawableFactory;
final IPackageManager mIpm;
final UserManager mUm;
+ final StorageStatsManager mStats;
final int mAdminRetrieveFlags;
final int mRetrieveFlags;
PackageIntentReceiver mPackageIntentReceiver;
boolean mResumed;
boolean mHaveDisabledApps;
+ boolean mHaveInstantApps;
// Information about all applications. Synchronize on mEntriesMap
// to protect access to these.
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
long mCurId = 1;
+ UUID mCurComputingSizeUuid;
String mCurComputingSizePkg;
int mCurComputingSizeUserId;
boolean mSessionsChanged;
private ApplicationsState(Application app) {
mContext = app;
mPm = mContext.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(mContext);
mIpm = AppGlobals.getPackageManager();
- mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
+ mUm = mContext.getSystemService(UserManager.class);
+ mStats = mContext.getSystemService(StorageStatsManager.class);
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
mEntriesMap.put(userId, new HashMap<String, AppEntry>());
}
mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
// Only the owner can see all apps.
- mAdminRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
- PackageManager.GET_DISABLED_COMPONENTS |
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
- mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS |
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
+ mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER |
+ PackageManager.MATCH_DISABLED_COMPONENTS |
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+ mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS |
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
/**
* This is a trick to prevent the foreground thread from being delayed.
}
mHaveDisabledApps = false;
+ mHaveInstantApps = false;
for (int i=0; i<mApplications.size(); i++) {
final ApplicationInfo info = mApplications.get(i);
// Need to trim out any applications that are disabled by
}
mHaveDisabledApps = true;
}
+ if (!mHaveInstantApps && AppUtils.isInstant(info)) {
+ mHaveInstantApps = true;
+ }
+
int userId = UserHandle.getUserId(info.uid);
final AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
if (entry != null) {
public boolean haveDisabledApps() {
return mHaveDisabledApps;
}
+ public boolean haveInstantApps() {
+ return mHaveInstantApps;
+ }
void doPauseIfNeededLocked() {
if (!mResumed) {
return;
}
synchronized (entry) {
- entry.ensureIconLocked(mContext, mPm);
+ entry.ensureIconLocked(mContext, mDrawableFactory);
}
}
if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock...");
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
- if (entry != null) {
- mPm.getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver);
+ if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ mBackgroundHandler.post(() -> {
+ try {
+ final StorageStats stats = mStats.queryStatsForPackage(
+ entry.info.storageUuid, packageName, UserHandle.of(userId));
+ final PackageStats legacy = new PackageStats(packageName, userId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+ });
}
if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
}
}
mHaveDisabledApps = true;
}
+ if (AppUtils.isInstant(info)) {
+ mHaveInstantApps = true;
+ }
mApplications.add(info);
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
mApplications.remove(idx);
if (!info.enabled) {
mHaveDisabledApps = false;
- for (int i=0; i<mApplications.size(); i++) {
- if (!mApplications.get(i).enabled) {
+ for (ApplicationInfo otherInfo : mApplications) {
+ if (!otherInfo.enabled) {
mHaveDisabledApps = true;
break;
}
}
}
+ if (AppUtils.isInstant(info)) {
+ mHaveInstantApps = false;
+ for (ApplicationInfo otherInfo : mApplications) {
+ if (AppUtils.isInstant(otherInfo)) {
+ mHaveInstantApps = true;
+ break;
+ }
+ }
+ }
if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) {
mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED);
}
}
if (filter != null) {
- filter.init();
+ filter.init(mContext);
}
List<AppEntry> apps;
}
if (comparator != null) {
- Collections.sort(filteredApps, comparator);
+ synchronized (mEntriesMap) {
+ // Locking to ensure that the background handler does not mutate
+ // the size of AppEntries used for ordering while sorting.
+ Collections.sort(filteredApps, comparator);
+ }
}
synchronized (mRebuildSync) {
// explicitly include both direct boot aware and unaware components here.
List<ResolveInfo> intents = mPm.queryIntentActivitiesAsUser(
launchIntent,
- PackageManager.GET_DISABLED_COMPONENTS
+ PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId
AppEntry entry = mAppEntries.get(i);
if (entry.icon == null || !entry.mounted) {
synchronized (entry) {
- if (entry.ensureIconLocked(mContext, mPm)) {
+ if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
long now = SystemClock.uptimeMillis();
for (int i=0; i<mAppEntries.size(); i++) {
AppEntry entry = mAppEntries.get(i);
- if (entry.size == SIZE_UNKNOWN || entry.sizeStale) {
+ if ((entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0
+ && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) {
if (entry.sizeLoadStart == 0 ||
(entry.sizeLoadStart < (now-20*1000))) {
if (!mRunning) {
mMainHandler.sendMessage(m);
}
entry.sizeLoadStart = now;
+ mCurComputingSizeUuid = entry.info.storageUuid;
mCurComputingSizePkg = entry.info.packageName;
mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
- mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg,
- mCurComputingSizeUserId, mStatsObserver);
+
+ mBackgroundHandler.post(() -> {
+ try {
+ final StorageStats stats = mStats.queryStatsForPackage(
+ mCurComputingSizeUuid, mCurComputingSizePkg,
+ UserHandle.of(mCurComputingSizeUserId));
+ final PackageStats legacy = new PackageStats(
+ mCurComputingSizePkg, mCurComputingSizeUserId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacy, true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ });
}
if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
return;
final IPackageStatsObserver.Stub mStatsObserver = new IPackageStatsObserver.Stub() {
public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
+ if (!succeeded) {
+ // There is no meaningful information in stats if the call failed.
+ return;
+ }
+
boolean sizeChanged = false;
synchronized (mEntriesMap) {
if (DEBUG_LOCKING) Log.v(TAG, "onGetStatsCompleted acquired lock");
}
}
- boolean ensureIconLocked(Context context, PackageManager pm) {
+ boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
if (this.icon == null) {
if (this.apkFile.exists()) {
- this.icon = getBadgedIcon(pm);
+ this.icon = drawableFactory.getBadgedIcon(info);
return true;
} else {
this.mounted = false;
- this.icon = context.getDrawable(
- com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
+ this.icon = context.getDrawable(R.drawable.sym_app_on_sd_unavailable_icon);
}
} else if (!this.mounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
if (this.apkFile.exists()) {
this.mounted = true;
- this.icon = getBadgedIcon(pm);
+ this.icon = drawableFactory.getBadgedIcon(info);
return true;
}
}
return false;
}
- private Drawable getBadgedIcon(PackageManager pm) {
- // Do badging ourself so that it comes from the user of the app not the current user.
- return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
- new UserHandle(UserHandle.getUserId(info.uid)));
- }
-
public String getVersion(Context context) {
try {
return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
public interface AppFilter {
void init();
+ default void init(Context context) {
+ init();
+ }
boolean filterApp(AppEntry info);
}
public static final AppFilter FILTER_PERSONAL = new AppFilter() {
private int mCurrentUser;
+ @Override
public void init() {
mCurrentUser = ActivityManager.getCurrentUser();
}
};
public static final AppFilter FILTER_WITHOUT_DISABLED_UNTIL_USED = new AppFilter() {
+ @Override
public void init() {
- // do nothings
+ // do nothing
}
@Override
public static final AppFilter FILTER_WORK = new AppFilter() {
private int mCurrentUser;
+ @Override
public void init() {
mCurrentUser = ActivityManager.getCurrentUser();
}
* Displays a combined list with "downloaded" and "visible in launcher" apps only.
*/
public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER = new AppFilter() {
+ @Override
public void init() {
}
@Override
public boolean filterApp(AppEntry entry) {
- if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ if (AppUtils.isInstant(entry.info)) {
+ return false;
+ } else if ((entry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
return true;
} else if ((entry.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true;
}
};
+ /**
+ * Displays a combined list with "downloaded" and "visible in launcher" apps only.
+ */
+ public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return AppUtils.isInstant(entry.info)
+ || FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry);
+ }
+
+ };
+
public static final AppFilter FILTER_THIRD_PARTY = new AppFilter() {
+ @Override
public void init() {
}
};
public static final AppFilter FILTER_DISABLED = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return !entry.info.enabled && !AppUtils.isInstant(entry.info);
+ }
+ };
+
+ public static final AppFilter FILTER_INSTANT = new AppFilter() {
+ @Override
public void init() {
}
@Override
public boolean filterApp(AppEntry entry) {
- return !entry.info.enabled;
+ return AppUtils.isInstant(entry.info);
}
};
public static final AppFilter FILTER_ALL_ENABLED = new AppFilter() {
+ @Override
public void init() {
}
@Override
public boolean filterApp(AppEntry entry) {
- return entry.info.enabled;
+ return entry.info.enabled && !AppUtils.isInstant(entry.info);
}
};
public static final AppFilter FILTER_EVERYTHING = new AppFilter() {
+ @Override
public void init() {
}
};
public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ return !AppUtils.isInstant(entry.info)
+ && (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ }
+ };
+
+ public static final AppFilter FILTER_NOT_HIDE = new AppFilter() {
+ private String[] mHidePackageNames;
+
+ @Override
+ public void init(Context context) {
+ mHidePackageNames = context.getResources()
+ .getStringArray(R.array.config_hideWhenDisabled_packageNames);
+ }
+
+ @Override
public void init() {
}
@Override
public boolean filterApp(AppEntry entry) {
- return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+ if (ArrayUtils.contains(mHidePackageNames, entry.info.packageName)) {
+ if (!entry.info.enabled) {
+ return false;
+ } else if (entry.info.enabledSetting ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+
+ public static final AppFilter FILTER_GAMES = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(ApplicationsState.AppEntry info) {
+ // TODO: Update for the new game category.
+ boolean isGame;
+ synchronized (info.info) {
+ isGame = ((info.info.flags & ApplicationInfo.FLAG_IS_GAME) != 0)
+ || info.info.category == ApplicationInfo.CATEGORY_GAME;
+ }
+ return isGame;
}
};
}
@Override
+ public void init(Context context) {
+ mFirstFilter.init(context);
+ mSecondFilter.init(context);
+ }
+
+ @Override
public void init() {
mFirstFilter.init();
mSecondFilter.init();
return mFirstFilter.filterApp(info) && mSecondFilter.filterApp(info);
}
}
+
+ public static final AppFilter FILTER_AUDIO = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isMusicApp;
+ synchronized(entry) {
+ isMusicApp = entry.info.category == ApplicationInfo.CATEGORY_AUDIO;
+ }
+ return isMusicApp;
+ }
+ };
+
+ public static final AppFilter FILTER_MOVIES = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isMovieApp;
+ synchronized(entry) {
+ isMovieApp = entry.info.category == ApplicationInfo.CATEGORY_VIDEO;
+ }
+ return isMovieApp;
+ }
+ };
+
+ public static final AppFilter FILTER_PHOTOS =
+ new AppFilter() {
+ @Override
+ public void init() {}
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isPhotosApp;
+ synchronized (entry) {
+ isPhotosApp = entry.info.category == ApplicationInfo.CATEGORY_IMAGE;
+ }
+ return isPhotosApp;
+ }
+ };
+
+ public static final AppFilter FILTER_OTHER_APPS =
+ new AppFilter() {
+ @Override
+ public void init() {}
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isCategorized;
+ synchronized (entry) {
+ isCategorized =
+ FILTER_AUDIO.filterApp(entry)
+ || FILTER_GAMES.filterApp(entry)
+ || FILTER_MOVIES.filterApp(entry)
+ || FILTER_PHOTOS.filterApp(entry);
+ }
+ return !isCategorized;
+ }
+ };
}