summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java30
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--services/core/java/com/android/server/job/controllers/AppIdleController.java174
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java9
-rw-r--r--services/core/java/com/android/server/job/controllers/StateController.java2
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java133
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java17
8 files changed, 365 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 7a74e45..c2b0a4d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -73,6 +73,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+ static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || true;
static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b0b410b..cdaa5a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -43,6 +43,7 @@ import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.res.Resources;
@@ -61,8 +62,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.SparseIntArray;
-
import android.view.Display;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.DumpHeapActivity;
@@ -96,7 +97,6 @@ import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -17854,6 +17854,10 @@ public final class ActivityManagerService extends ActivityManagerNative
app.lastCpuTime = app.curCpuTime;
}
+ // Inform UsageStats of important process state change
+ // Must be called before updating setProcState
+ maybeUpdateUsageStats(app);
+
app.setProcState = app.curProcState;
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
app.notCachedSinceIdle = false;
@@ -17916,6 +17920,28 @@ public final class ActivityManagerService extends ActivityManagerNative
return success;
}
+ private void maybeUpdateUsageStats(ProcessRecord app) {
+ if (DEBUG_USAGE_STATS) {
+ Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+ + "] state changes: old = " + app.setProcState + ", new = "
+ + app.curProcState);
+ }
+ if (mUsageStatsService == null) {
+ return;
+ }
+ if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ && (app.setProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ || app.setProcState < 0)) {
+ String[] packages = app.getPackageList();
+ if (packages != null) {
+ for (int i = 0; i < packages.length; i++) {
+ mUsageStatsService.reportEvent(packages[i], app.userId,
+ UsageEvents.Event.INTERACTION);
+ }
+ }
+ }
+ }
+
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null) {
if (proc.baseProcessTracker != null) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d79b5fd..ecda36a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -51,6 +51,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.app.IBatteryStats;
+import com.android.server.job.controllers.AppIdleController;
import com.android.server.job.controllers.BatteryController;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.IdleController;
@@ -317,6 +318,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(TimeController.get(this));
mControllers.add(IdleController.get(this));
mControllers.add(BatteryController.get(this));
+ mControllers.add(AppIdleController.get(this));
mHandler = new JobHandler(context.getMainLooper());
mJobSchedulerStub = new JobSchedulerStub();
@@ -688,7 +690,6 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
final boolean userRunning = mStartedUsers.contains(job.getUserId());
-
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " ready=" + jobReady + " pending=" + jobPending
@@ -738,6 +739,10 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
if (availableContext != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job "
+ + nextPending.getJob().getService().toString());
+ }
if (!availableContext.executeRunnableJob(nextPending)) {
if (DEBUG) {
Slog.d(TAG, "Error executing " + nextPending);
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
new file mode 100644
index 0000000..03e9ad5
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 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.job.controllers;
+
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Controls when apps are considered idle and if jobs pertaining to those apps should
+ * be executed. Apps that haven't been actively launched or accessed from a foreground app
+ * for a certain amount of time (maybe hours or days) are considered idle. When the app comes
+ * out of idle state, it will be allowed to run scheduled jobs.
+ */
+public class AppIdleController extends StateController
+ implements UsageStatsManagerInternal.AppIdleStateChangeListener {
+
+ private static final String LOG_TAG = "AppIdleController";
+ private static final boolean DEBUG = true;
+
+ // Singleton factory
+ private static Object sCreationLock = new Object();
+ private static volatile AppIdleController sController;
+ final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
+ private final UsageStatsManagerInternal mUsageStatsInternal;
+ private final BatteryManagerInternal mBatteryManagerInternal;
+ private boolean mPluggedIn;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+ // TODO: Allow any charger type
+ onPluggedIn((plugged & BatteryManager.BATTERY_PLUGGED_AC) != 0);
+ }
+ }
+ };
+
+ public static AppIdleController get(JobSchedulerService service) {
+ synchronized (sCreationLock) {
+ if (sController == null) {
+ sController = new AppIdleController(service, service.getContext());
+ }
+ return sController;
+ }
+ }
+
+ private AppIdleController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ mPluggedIn = isPowered();
+ mUsageStatsInternal.addAppIdleStateChangeListener(this);
+ registerReceivers();
+ }
+
+ private void registerReceivers() {
+ // Monitor battery charging state
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private boolean isPowered() {
+ // TODO: Allow any charger type
+ return mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_AC);
+ }
+
+ @Override
+ public void maybeStartTrackingJob(JobStatus jobStatus) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.add(jobStatus);
+ String packageName = jobStatus.job.getService().getPackageName();
+ final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName,
+ jobStatus.getUserId());
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+ + packageName + " to " + appIdle);
+ }
+ jobStatus.appNotIdleConstraintSatisfied.set(!appIdle);
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJob(JobStatus jobStatus) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.remove(jobStatus);
+ }
+ }
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+ // TODO:
+ }
+
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ boolean changed = false;
+ synchronized (mTrackedTasks) {
+ // If currently plugged in, we don't care about app idle state
+ if (mPluggedIn) {
+ return;
+ }
+ for (JobStatus task : mTrackedTasks) {
+ if (task.job.getService().getPackageName().equals(packageName)
+ && task.getUserId() == userId) {
+ if (task.appNotIdleConstraintSatisfied.get() != !idle) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
+ + packageName + " to " + idle);
+ }
+ task.appNotIdleConstraintSatisfied.set(!idle);
+ changed = true;
+ }
+ }
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ void onPluggedIn(boolean pluggedIn) {
+ // Flag if any app's idle state has changed
+ boolean changed = false;
+ synchronized (mTrackedTasks) {
+ if (mPluggedIn == pluggedIn) {
+ return;
+ }
+ mPluggedIn = pluggedIn;
+ for (JobStatus task : mTrackedTasks) {
+ String packageName = task.job.getService().getPackageName();
+ final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName,
+ task.getUserId());
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Plugged in " + pluggedIn + ", setting idle state of "
+ + packageName + " to " + appIdle);
+ }
+ if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
+ task.appNotIdleConstraintSatisfied.set(!appIdle);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e3c55b6..69c63f3 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -54,6 +54,7 @@ public class JobStatus {
final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean appNotIdleConstraintSatisfied = new AtomicBoolean();
/**
* Earliest point in the future at which this job will be eligible to run. A value of 0
@@ -199,8 +200,11 @@ public class JobStatus {
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
*/
public synchronized boolean isReady() {
- return isConstraintsSatisfied()
- || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
+ // Deadline constraint trumps other constraints
+ // AppNotIdle implicit constraint trumps all!
+ return (isConstraintsSatisfied()
+ || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get()))
+ && appNotIdleConstraintSatisfied.get();
}
/**
@@ -229,6 +233,7 @@ public class JobStatus {
+ ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging()
+ ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
+ ",P=" + job.isPersisted()
+ + ",ANI=" + appNotIdleConstraintSatisfied.get()
+ (isReady() ? "(READY)" : "")
+ "]";
}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index efd1928..cda7c32 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -44,7 +44,7 @@ public abstract class StateController {
/**
* Implement the logic here to decide whether a job should be tracked by this controller.
- * This logic is put here so the JobManger can be completely agnostic of Controller logic.
+ * This logic is put here so the JobManager can be completely agnostic of Controller logic.
* Also called when updating a task, so implementing controllers have to be aware of
* preexisting tasks.
*/
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5eefe6a..f458dbc 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -21,8 +21,10 @@ import android.app.AppOpsManager;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +34,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -42,6 +46,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -53,6 +58,7 @@ import com.android.server.SystemService;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -74,6 +80,7 @@ public class UsageStatsService extends SystemService implements
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
+ static final int MSG_INFORM_LISTENERS = 3;
private final Object mLock = new Object();
Handler mHandler;
@@ -85,6 +92,12 @@ public class UsageStatsService extends SystemService implements
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
+ private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 3L * 24 * 60 * 60 * 1000; //3 days
+ private long mAppIdleDurationMillis;
+
+ private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+ mPackageAccessListeners = new ArrayList<>();
+
public UsageStatsService(Context context) {
super(context);
}
@@ -112,11 +125,24 @@ public class UsageStatsService extends SystemService implements
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
+ // Look at primary user's secure setting for this. TODO: Maybe apply different
+ // thresholds for different users.
+ mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
+ Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
+ UserHandle.USER_OWNER);
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // Observe changes to the threshold
+ new SettingsObserver(mHandler).registerObserver();
+ }
+ }
+
private class UserRemovedReceiver extends BroadcastReceiver {
@Override
@@ -235,7 +261,19 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ final long lastUsed = service.getLastPackageAccessTime(event.mPackage);
+ final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
service.reportEvent(event);
+ // Inform listeners if necessary
+ if ((event.mEventType == Event.MOVE_TO_FOREGROUND
+ || event.mEventType == Event.MOVE_TO_BACKGROUND
+ || event.mEventType == Event.INTERACTION)) {
+ if (previouslyIdle) {
+ // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ 0, event.mPackage));
+ }
+ }
}
}
@@ -308,6 +346,53 @@ public class UsageStatsService extends SystemService implements
}
}
+ /**
+ * Called by LocalService stub.
+ */
+ long getLastPackageAccessTime(String packageName, int userId) {
+ synchronized (mLock) {
+ final long timeNow = checkAndGetTimeLocked();
+ // android package is always considered non-idle.
+ // TODO: Add a generic whitelisting mechanism
+ if (packageName.equals("android")) {
+ return timeNow;
+ }
+ final UserUsageStatsService service =
+ getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ return service.getLastPackageAccessTime(packageName);
+ }
+ }
+
+ void addListener(AppIdleStateChangeListener listener) {
+ synchronized (mLock) {
+ if (!mPackageAccessListeners.contains(listener)) {
+ mPackageAccessListeners.add(listener);
+ }
+ }
+ }
+
+ void removeListener(AppIdleStateChangeListener listener) {
+ synchronized (mLock) {
+ mPackageAccessListeners.remove(listener);
+ }
+ }
+
+ private boolean hasPassedIdleDuration(long lastUsed) {
+ final long now = System.currentTimeMillis();
+ return lastUsed < now - mAppIdleDurationMillis;
+ }
+
+ boolean isAppIdle(String packageName, int userId) {
+ final long lastUsed = getLastPackageAccessTime(packageName, userId);
+ return hasPassedIdleDuration(lastUsed);
+ }
+
+ void informListeners(String packageName, int userId, boolean isIdle) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ }
+ }
+
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
@@ -366,6 +451,10 @@ public class UsageStatsService extends SystemService implements
removeUser(msg.arg1);
break;
+ case MSG_INFORM_LISTENERS:
+ informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -373,6 +462,29 @@ public class UsageStatsService extends SystemService implements
}
}
+ /**
+ * Observe settings changes for Settings.Secure.APP_IDLE_DURATION.
+ */
+ private class SettingsObserver extends ContentObserver {
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void registerObserver() {
+ getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.APP_IDLE_DURATION), false, this, UserHandle.USER_OWNER);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
+ Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
+ UserHandle.USER_OWNER);
+ // TODO: Check if we need to update idle states of all the apps
+ }
+ }
+
private class BinderService extends IUsageStatsManager.Stub {
private boolean hasPermission(String callingPackage) {
@@ -523,11 +635,32 @@ public class UsageStatsService extends SystemService implements
}
@Override
+ public boolean isAppIdle(String packageName, int userId) {
+ return UsageStatsService.this.isAppIdle(packageName, userId);
+ }
+
+ @Override
+ public long getLastPackageAccessTime(String packageName, int userId) {
+ return UsageStatsService.this.getLastPackageAccessTime(packageName, userId);
+ }
+
+ @Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
// we are shutting down.
shutdown();
}
+
+ @Override
+ public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
+ UsageStatsService.this.addListener(listener);
+ }
+
+ @Override
+ public void removeAppIdleStateChangeListener(
+ AppIdleStateChangeListener listener) {
+ UsageStatsService.this.removeListener(listener);
+ }
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 75fa030..afe27c7 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -65,7 +65,8 @@ class UserUsageStatsService {
void onStatsUpdated();
}
- UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) {
+ UserUsageStatsService(Context context, int userId, File usageStatsDir,
+ StatsUpdatedListener listener) {
mContext = context;
mDailyExpiryDate = new UnixCalendar(0);
mDatabase = new UsageStatsDatabase(usageStatsDir);
@@ -161,7 +162,9 @@ class UserUsageStatsService {
if (currentDailyStats.events == null) {
currentDailyStats.events = new TimeSparseArray<>();
}
- currentDailyStats.events.put(event.mTimeStamp, event);
+ if (event.mEventType != UsageEvents.Event.INTERACTION) {
+ currentDailyStats.events.put(event.mTimeStamp, event);
+ }
for (IntervalStats stats : mCurrentStats) {
if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
@@ -328,6 +331,16 @@ class UserUsageStatsService {
return new UsageEvents(results, table);
}
+ long getLastPackageAccessTime(String packageName) {
+ final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
+ UsageStats packageUsage;
+ if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+ return -1;
+ } else {
+ return packageUsage.getLastTimeUsed();
+ }
+ }
+
void persistActiveStats() {
if (mStatsChanged) {
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");