diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c63d2aaecfdec..62001aabca765 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5045,10 +5045,6 @@ android:permission="android.permission.BIND_JOB_SERVICE" > - - - diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index 2f8c506d5ea76..6641b5be651d6 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -281,13 +281,4 @@ public abstract class UsageStatsManagerInternal { return mUsageRemaining; } } - - /** - * Called by {@link com.android.server.usage.UsageStatsIdleService} when the device is idle to - * prune usage stats data for uninstalled packages. - * - * @param userId the user associated with the job - * @return {@code true} if the pruning was successful, {@code false} otherwise - */ - public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId); } diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index e6bb244ef05bf..e32103fe6bff7 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -45,7 +45,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Locale; -import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest @@ -94,8 +93,6 @@ public class UsageStatsDatabaseTest { for (File f : usageFiles) { f.delete(); } - } else { - intervalDir.delete(); } } } @@ -590,7 +587,6 @@ public class UsageStatsDatabaseTest { db.readMappingsLocked(); db.init(1); db.putUsageStats(interval, mIntervalStats); - db.writeMappingsLocked(); final String removedPackage = "fake.package.name0"; // invoke handler call directly from test to remove package @@ -598,19 +594,19 @@ public class UsageStatsDatabaseTest { List stats = db.queryUsageStats(interval, 0, mEndTime, mIntervalStatsVerifier); - assertEquals(1, stats.size(), - "Only one interval stats object should exist for the given time range."); - final IntervalStats stat = stats.get(0); - if (stat.packageStats.containsKey(removedPackage)) { - fail("Found removed package " + removedPackage + " in package stats."); - return; - } - for (int i = 0; i < stat.events.size(); i++) { - final Event event = stat.events.get(i); - if (removedPackage.equals(event.mPackage)) { - fail("Found an event from removed package " + removedPackage); + for (int i = 0; i < stats.size(); i++) { + final IntervalStats stat = stats.get(i); + if (stat.packageStats.containsKey(removedPackage)) { + fail("Found removed package " + removedPackage + " in package stats."); return; } + for (int j = 0; j < stat.events.size(); j++) { + final Event event = stat.events.get(j); + if (removedPackage.equals(event.mPackage)) { + fail("Found an event from removed package " + removedPackage); + return; + } + } } } @@ -621,90 +617,4 @@ public class UsageStatsDatabaseTest { verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY); verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY); } - - private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval, - String removedPackage) { - List stats = db.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); - assertEquals(1, stats.size(), - "Only one interval stats object should exist for the given time range."); - final IntervalStats stat = stats.get(0); - if (stat.packageStats.containsKey(removedPackage)) { - fail("Found removed package " + removedPackage + " in package stats."); - return; - } - for (int i = 0; i < stat.events.size(); i++) { - final Event event = stat.events.get(i); - if (removedPackage.equals(event.mPackage)) { - fail("Found an event from removed package " + removedPackage); - return; - } - } - } - - private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval, - Set installedPackages) { - List stats = db.queryUsageStats(interval, 0, mEndTime, - mIntervalStatsVerifier); - assertEquals(1, stats.size(), - "Only one interval stats object should exist for the given time range."); - final IntervalStats stat = stats.get(0); - if (!stat.packageStats.containsAll(installedPackages)) { - fail("Could not find some installed packages in package stats."); - return; - } - // attempt to find an event from each installed package - for (String installedPackage : installedPackages) { - for (int i = 0; i < stat.events.size(); i++) { - if (installedPackage.equals(stat.events.get(i).mPackage)) { - break; - } - if (i == stat.events.size() - 1) { - fail("Could not find any event for: " + installedPackage); - return; - } - } - } - } - - @Test - public void testPackageDataIsRemoved() throws IOException { - UsageStatsDatabase db = new UsageStatsDatabase(mTestDir); - db.readMappingsLocked(); - db.init(1); - - // write stats to disk for each interval - db.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats); - db.putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, mIntervalStats); - db.putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, mIntervalStats); - db.putUsageStats(UsageStatsManager.INTERVAL_YEARLY, mIntervalStats); - db.writeMappingsLocked(); - - final Set installedPackages = mIntervalStats.packageStats.keySet(); - final String removedPackage = installedPackages.iterator().next(); - installedPackages.remove(removedPackage); - - // mimic a package uninstall - db.onPackageRemoved(removedPackage, System.currentTimeMillis()); - - // mimic the idle prune job being triggered - db.pruneUninstalledPackagesData(); - - // read data from disk into a new db instance - UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir); - newDB.readMappingsLocked(); - newDB.init(mEndTime); - - // query data for each interval and ensure data for package doesn't exist - verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, removedPackage); - verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, removedPackage); - verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, removedPackage); - verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, removedPackage); - - // query data for each interval and ensure some data for installed packages exists - verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, installedPackages); - verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, installedPackages); - verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, installedPackages); - verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, installedPackages); - } } diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 8fb283adc7405..46b261b64192d 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -448,11 +448,8 @@ public class IntervalStats { /** * Parses all of the tokens to strings in the obfuscated usage stats data. This includes * deobfuscating each of the package tokens and chooser actions and categories. - * - * @return {@code true} if any stats were omitted while deobfuscating, {@code false} otherwise. */ - private boolean deobfuscateUsageStats(PackagesTokenData packagesTokenData) { - boolean dataOmitted = false; + private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) { final int usageStatsSize = packageStatsObfuscated.size(); for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) { final int packageToken = packageStatsObfuscated.keyAt(statsIndex); @@ -460,7 +457,6 @@ public class IntervalStats { usageStats.mPackageName = packagesTokenData.getPackageString(packageToken); if (usageStats.mPackageName == null) { Slog.e(TAG, "Unable to parse usage stats package " + packageToken); - dataOmitted = true; continue; } @@ -493,18 +489,14 @@ public class IntervalStats { } packageStats.put(usageStats.mPackageName, usageStats); } - return dataOmitted; } /** * Parses all of the tokens to strings in the obfuscated events data. This includes * deobfuscating the package token, along with any class, task root package/class tokens, and * shortcut or notification channel tokens. - * - * @return {@code true} if any events were omitted while deobfuscating, {@code false} otherwise. */ - private boolean deobfuscateEvents(PackagesTokenData packagesTokenData) { - boolean dataOmitted = false; + private void deobfuscateEvents(PackagesTokenData packagesTokenData) { for (int i = this.events.size() - 1; i >= 0; i--) { final Event event = this.events.get(i); final int packageToken = event.mPackageToken; @@ -512,7 +504,6 @@ public class IntervalStats { if (event.mPackage == null) { Slog.e(TAG, "Unable to parse event package " + packageToken); this.events.remove(i); - dataOmitted = true; continue; } @@ -552,7 +543,6 @@ public class IntervalStats { Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken + " for package " + packageToken); this.events.remove(i); - dataOmitted = true; continue; } break; @@ -564,25 +554,21 @@ public class IntervalStats { + event.mNotificationChannelIdToken + " for package " + packageToken); this.events.remove(i); - dataOmitted = true; continue; } break; } } - return dataOmitted; } /** * Parses the obfuscated tokenized data held in this interval stats object. * - * @return {@code true} if any data was omitted while deobfuscating, {@code false} otherwise. * @hide */ - public boolean deobfuscateData(PackagesTokenData packagesTokenData) { - final boolean statsOmitted = deobfuscateUsageStats(packagesTokenData); - final boolean eventsOmitted = deobfuscateEvents(packagesTokenData); - return statsOmitted || eventsOmitted; + public void deobfuscateData(PackagesTokenData packagesTokenData) { + deobfuscateUsageStats(packagesTokenData); + deobfuscateEvents(packagesTokenData); } /** diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java index f19abbbac485b..4bf08a49af0f1 100644 --- a/services/usage/java/com/android/server/usage/PackagesTokenData.java +++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java @@ -162,18 +162,15 @@ public final class PackagesTokenData { * * @param packageName the package to be removed * @param timeRemoved the time stamp of when the package was removed - * @return the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN} - * if not mapped */ - public int removePackage(String packageName, long timeRemoved) { + public void removePackage(String packageName, long timeRemoved) { removedPackagesMap.put(packageName, timeRemoved); if (!packagesToTokensMap.containsKey(packageName)) { - return UNASSIGNED_TOKEN; + return; } final int packageToken = packagesToTokensMap.get(packageName).get(packageName); packagesToTokensMap.remove(packageName); tokensToPackagesMap.delete(packageToken); - return packageToken; } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index d938baddd4215..27d7360313ad5 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -545,52 +545,9 @@ public class UsageStatsDatabase { } } - /** - * Returns the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN} - * if not mapped. - */ - int onPackageRemoved(String packageName, long timeRemoved) { + void onPackageRemoved(String packageName, long timeRemoved) { synchronized (mLock) { - return mPackagesTokenData.removePackage(packageName, timeRemoved); - } - } - - /** - * Reads all the usage stats data on disk and rewrites it with any data related to uninstalled - * packages omitted. - */ - boolean pruneUninstalledPackagesData() { - synchronized (mLock) { - for (int i = 0; i < mIntervalDirs.length; i++) { - final File[] files = mIntervalDirs[i].listFiles(); - if (files == null) { - continue; - } - for (int j = 0; j < files.length; j++) { - try { - final IntervalStats stats = new IntervalStats(); - final AtomicFile atomicFile = new AtomicFile(files[j]); - if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData)) { - continue; // no data was omitted when read so no need to rewrite - } - // Any data related to packages that have been removed would have failed - // the deobfuscation step on read so the IntervalStats object here only - // contains data for packages that are currently installed - all we need - // to do here is write the data back to disk. - writeLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData); - } catch (Exception e) { - Slog.e(TAG, "Failed to prune data from: " + files[j].toString()); - return false; - } - } - } - try { - writeMappingsLocked(); - } catch (IOException e) { - Slog.e(TAG, "Failed to write package mappings after pruning data."); - return false; - } - return true; + mPackagesTokenData.removePackage(packageName, timeRemoved); } } @@ -688,6 +645,7 @@ public class UsageStatsDatabase { } // filter out events + final int eventsSize = stats.events.size(); for (int i = stats.events.size() - 1; i >= 0; i--) { final UsageEvents.Event event = stats.events.get(i); final Long timeRemoved = removedPackagesMap.get(event.mPackage); @@ -984,17 +942,13 @@ public class UsageStatsDatabase { readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData); } - /** - * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise. - */ - private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version, + private static void readLocked(AtomicFile file, IntervalStats statsOut, int version, PackagesTokenData packagesTokenData) throws IOException { - boolean dataOmitted = false; try { FileInputStream in = file.openRead(); try { statsOut.beginTime = parseBeginTime(file); - dataOmitted = readLocked(in, statsOut, version, packagesTokenData); + readLocked(in, statsOut, version, packagesTokenData); statsOut.lastTimeSaved = file.getLastModifiedTime(); } finally { try { @@ -1007,15 +961,10 @@ public class UsageStatsDatabase { Slog.e(TAG, "UsageStatsDatabase", e); throw e; } - return dataOmitted; } - /** - * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise. - */ - private static boolean readLocked(InputStream in, IntervalStats statsOut, int version, + private static void readLocked(InputStream in, IntervalStats statsOut, int version, PackagesTokenData packagesTokenData) throws IOException { - boolean dataOmitted = false; switch (version) { case 1: case 2: @@ -1040,14 +989,14 @@ public class UsageStatsDatabase { } catch (IOException e) { Slog.e(TAG, "Unable to read interval stats from proto.", e); } - dataOmitted = statsOut.deobfuscateData(packagesTokenData); + statsOut.deobfuscateData(packagesTokenData); break; default: throw new RuntimeException( "Unhandled UsageStatsDatabase version: " + Integer.toString(version) + " on read."); } - return dataOmitted; + } /** diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java deleted file mode 100644 index 8677779da978c..0000000000000 --- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.usage; - -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.app.usage.UsageStatsManagerInternal; -import android.content.ComponentName; -import android.content.Context; -import android.os.AsyncTask; -import android.os.PersistableBundle; - -import com.android.server.LocalServices; - -/** - * JobService used to do any work for UsageStats while the device is idle. - */ -public class UsageStatsIdleService extends JobService { - - /** - * Base job ID for the pruning job - must be unique within the system server uid. - */ - private static final int PRUNE_JOB_ID = 546357475; - - private static final String USER_ID_KEY = "user_id"; - private static final String REMOVED_TOKENS_KEY = "removed_tokens"; - - static void scheduleJob(Context context, int userId, int token) { - final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user - final ComponentName component = new ComponentName(context.getPackageName(), - UsageStatsIdleService.class.getName()); - final PersistableBundle bundle = new PersistableBundle(); - bundle.putInt(USER_ID_KEY, userId); - bundle.putIntArray(REMOVED_TOKENS_KEY, - getOrCreateRemovedTokens(fetchRemovedTokens(context, userId), token)); - final JobInfo pruneJob = new JobInfo.Builder(userJobId, component) - .setRequiresDeviceIdle(true) - .setExtras(bundle) - .setPersisted(true) - .build(); - - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); - jobScheduler.schedule(pruneJob); - } - - static void cancelJob(Context context, int userId) { - final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); - jobScheduler.cancel(userJobId); - } - - /** - * Fetches an array of removed tokens from previous prune jobs, if any. - */ - static int[] fetchRemovedTokens(Context context, int userId) { - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); - final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user - final JobInfo pendingJob = jobScheduler.getPendingJob(userJobId); - if (pendingJob != null) { - final PersistableBundle bundle = pendingJob.getExtras(); - return bundle.getIntArray(REMOVED_TOKENS_KEY); - } - return null; - } - - @Override - public boolean onStartJob(JobParameters params) { - final PersistableBundle bundle = params.getExtras(); - final int userId = bundle.getInt(USER_ID_KEY, -1); - if (userId == -1) { - return false; - } - - AsyncTask.execute(() -> { - final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService( - UsageStatsManagerInternal.class); - final boolean pruned = usageStatsManagerInternal.pruneUninstalledPackagesData(userId); - jobFinished(params, !pruned); // reschedule if data was not pruned - }); - return true; - } - - @Override - public boolean onStopJob(JobParameters params) { - // Since the pruning job isn't a heavy job, we don't want to cancel it's execution midway. - return false; - } - - /** - * Helper method to create a cloned array of removed tokens from previous jobs (if any) with - * the newly removed token at index 0. If there are no removed tokens from previous jobs, it - * simply returns a new array containing the current token. - */ - private static int[] getOrCreateRemovedTokens(int[] previousRemovedTokens, int token) { - final int[] removedTokens; - if (previousRemovedTokens == null) { - removedTokens = new int[1]; - } else { - removedTokens = new int[previousRemovedTokens.length + 1]; - System.arraycopy(previousRemovedTokens, 0, removedTokens, 1, - previousRemovedTokens.length); - } - removedTokens[0] = token; - return removedTokens; - } -} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 5f5e7e8de1d1a..f9b365906c5b6 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -846,8 +846,6 @@ public class UsageStatsService extends SystemService implements mUserState.remove(userId); mAppStandby.onUserRemoved(userId); mAppTimeLimit.onUserRemoved(userId); - // Cancel any scheduled jobs for this user since the user is being removed. - UsageStatsIdleService.cancelJob(getContext(), userId); } } @@ -857,7 +855,7 @@ public class UsageStatsService extends SystemService implements private void onPackageRemoved(int userId, String packageName) { synchronized (mLock) { final long timeRemoved = System.currentTimeMillis(); - if (!mUserUnlockedStates.get(userId)) { + if (!mUserUnlockedStates.get(userId, false)) { // If user is not unlocked and a package is removed for them, we will handle it // when the user service is initialized and package manager is queried. return; @@ -871,24 +869,6 @@ public class UsageStatsService extends SystemService implements } } - /** - * Called by the Binder stub. - */ - private boolean pruneUninstalledPackagesData(int userId) { - synchronized (mLock) { - if (!mUserUnlockedStates.get(userId)) { - return false; // user is no longer unlocked - } - - final UserUsageStatsService userService = mUserState.get(userId); - if (userService == null) { - return false; // user was stopped or removed - } - - return userService.pruneUninstalledPackagesData(); - } - } - /** * Called by the Binder stub. */ @@ -2144,11 +2124,6 @@ public class UsageStatsService extends SystemService implements public AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user) { return mAppTimeLimit.getAppUsageLimit(packageName, user); } - - @Override - public boolean pruneUninstalledPackagesData(int userId) { - return UsageStatsService.this.pruneUninstalledPackagesData(userId); - } } private class MyPackageMonitor extends PackageMonitor { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 20c0bd6748a66..5783932db4889 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -175,46 +175,14 @@ class UserUsageStatsService { } void onPackageRemoved(String packageName, long timeRemoved) { - final int token = mDatabase.onPackageRemoved(packageName, timeRemoved); - if (token != PackagesTokenData.UNASSIGNED_TOKEN) { - UsageStatsIdleService.scheduleJob(mContext, mUserId, token); - } + mDatabase.onPackageRemoved(packageName, timeRemoved); } private void readPackageMappingsLocked() { mDatabase.readMappingsLocked(); - updatePackageMappingsLocked(); cleanUpPackageMappingsLocked(); } - /** - * Queries Job Scheduler for any pending data prune jobs and if any exist, it updates the - * package mappings in memory by removing those tokens. - * This will only happen once per device boot, when the user is unlocked for the first time. - */ - private void updatePackageMappingsLocked() { - final long timeNow = System.currentTimeMillis(); - final int[] removedTokens = UsageStatsIdleService.fetchRemovedTokens(mContext, mUserId); - if (removedTokens == null) { - return; - } - - for (int i = removedTokens.length - 1; i >= 0; i--) { - final String packageName = - mDatabase.mPackagesTokenData.getPackageString(removedTokens[i]); - if (packageName == null) { - continue; - } - /* - Note: in most cases, packageName returned will be null since the package mappings file - on disk should have been updated when it was last persisted. This is to handle the rare - case of system crashing after a package was removed but the package mappings file - wasn't persisted to disk. - */ - mDatabase.mPackagesTokenData.removePackage(packageName, timeNow); - } - } - /** * Queries Package Manager for a list of installed packages and removes those packages from * mPackagesTokenData which are not installed any more. @@ -254,10 +222,6 @@ class UserUsageStatsService { } } - boolean pruneUninstalledPackagesData() { - return mDatabase.pruneUninstalledPackagesData(); - } - private void onTimeChanged(long oldTime, long newTime) { persistActiveStats(); mDatabase.onTimeChanged(newTime - oldTime);