diff options
12 files changed, 133 insertions, 48 deletions
diff --git a/api/current.txt b/api/current.txt index e2ea649e2242..87f0659cb982 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7682,6 +7682,7 @@ package android.app.usage { field public static final int ACTIVITY_RESUMED = 1; // 0x1 field public static final int ACTIVITY_STOPPED = 23; // 0x17 field public static final int CONFIGURATION_CHANGE = 5; // 0x5 + field public static final int DEVICE_SHUTDOWN = 26; // 0x1a field public static final int FOREGROUND_SERVICE_START = 19; // 0x13 field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14 field public static final int KEYGUARD_HIDDEN = 18; // 0x12 diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 1f01e2698fb5..5cac0489e8df 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -320,4 +320,10 @@ public abstract class ActivityManagerInternal { /** Remove pending backup for the given userId. */ public abstract void clearPendingBackup(int userId); + + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + public abstract void prepareForPossibleShutdown(); } diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java index a79ad2fc8607..aaae57e526a0 100644 --- a/core/java/android/app/usage/EventList.java +++ b/core/java/android/app/usage/EventList.java @@ -103,21 +103,4 @@ public class EventList { } return result; } - - /** - * Remove events of certain type on or after a timestamp. - * @param type The type of event to remove. - * @param timeStamp the timeStamp on or after which to remove the event. - */ - public void removeOnOrAfter(int type, long timeStamp) { - for (int i = mEvents.size() - 1; i >= 0; i--) { - UsageEvents.Event event = mEvents.get(i); - if (event.mTimeStamp < timeStamp) { - break; - } - if (event.mEventType == type) { - mEvents.remove(i); - } - } - } } diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index d7a53281bc56..2c5fe046faad 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -245,10 +245,18 @@ public final class UsageEvents implements Parcelable { public static final int FLUSH_TO_DISK = 25; /** + * An event type denoting that the device underwent a shutdown process. + * A DEVICE_SHUTDOWN event should be treated as if all started activities and foreground + * services are now stopped and no explicit {@link #ACTIVITY_STOPPED} and + * {@link #FOREGROUND_SERVICE_STOP} events will be generated for them. + */ + public static final int DEVICE_SHUTDOWN = 26; + + /** * Keep in sync with the greatest event type value. * @hide */ - public static final int MAX_EVENT_TYPE = 25; + public static final int MAX_EVENT_TYPE = 26; /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 308180badbb8..94a2a3eaae7f 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -119,12 +120,9 @@ public final class UsageStats implements Parcelable { public int mLastEvent; /** - * If an activity is visible(onStart(), onPause() states) or in foreground (onResume() state), - * it has one entry in this map. When an activity becomes invisible (onStop() or onDestroy()), - * it is removed from this map. * Key is instanceId of the activity (ActivityRecode appToken hashCode).. - * Value is this activity's last event, one of ACTIVITY_RESUMED or - * ACTIVITY_PAUSED. + * Value is this activity's last event, one of ACTIVITY_RESUMED, ACTIVITY_PAUSED or + * ACTIVITY_STOPPED. * {@hide} */ public SparseIntArray mActivities = new SparseIntArray(); @@ -560,6 +558,7 @@ public final class UsageStats implements Parcelable { mLastTimeForegroundServiceUsed = timeStamp; mForegroundServices.put(className, eventType); break; + case DEVICE_SHUTDOWN: case FLUSH_TO_DISK: // update usage of all active activities/services. if (hasForegroundActivity()) { diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 2edad350e18e..cc3ab0025864 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -98,6 +98,12 @@ public abstract class UsageStatsManagerInternal { public abstract void prepareShutdown(); /** + * When the device power button is long pressed for 3.5 seconds, prepareForPossibleShutdown() + * is called. + */ + public abstract void prepareForPossibleShutdown(); + + /** * Returns true if the app has not been used for a certain amount of time. How much time? * Could be hours, could be days, who knows? * diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java index 28aaf1e05644..f8147cfbce89 100644 --- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java +++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java @@ -21,6 +21,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -528,6 +529,11 @@ public class UsageStatsTest { } @Test + public void testEvent_DEVICE_SHUTDOWN() { + testClosingEvent(DEVICE_SHUTDOWN); + } + + @Test public void testEvent_FLUSH_TO_DISK() { testClosingEvent(FLUSH_TO_DISK); } @@ -535,8 +541,9 @@ public class UsageStatsTest { private void testClosingEvent(int eventType) { // When these three closing events are received, all open activities/services need to be // closed and usage stats are updated. - if (eventType != FLUSH_TO_DISK) { - fail("Closing eventType must be one of FLUSH_TO_DISK"); + if (eventType != DEVICE_SHUTDOWN + && eventType != FLUSH_TO_DISK) { + fail("Closing eventType must be one of DEVICE_SHUTDOWN, FLUSH_TO_DISK"); } left.mPackageName = "com.test"; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 43deb1176fe6..089847d1ff7f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17867,6 +17867,15 @@ public class ActivityManagerService extends IActivityManager.Stub public void clearPendingBackup(int userId) { ActivityManagerService.this.clearPendingBackup(userId); } + + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + @Override + public void prepareForPossibleShutdown() { + ActivityManagerService.this.prepareForPossibleShutdown(); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { @@ -18123,6 +18132,18 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * When power button is very long pressed, call this interface to do some pre-shutdown work + * like persisting database etc. + */ + public void prepareForPossibleShutdown() { + synchronized (this) { + if (mUsageStatsService != null) { + mUsageStatsService.prepareForPossibleShutdown(); + } + } + } + @VisibleForTesting public static class Injector { private NetworkManagementInternal mNmi; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2060aef37044..4bc241665023 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -84,8 +84,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs + .CAMERA_LENS_COVER_ABSENT; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs + .CAMERA_LENS_UNCOVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -808,6 +810,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + private Runnable mPossibleVeryLongPressReboot = new Runnable() { + @Override + public void run() { + mActivityManagerInternal.prepareForPossibleShutdown(); + } + }; + private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -953,6 +962,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); + schedulePossibleVeryLongPressReboot(); + // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered @@ -1056,6 +1067,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (hasVeryLongPressOnPowerBehavior()) { mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); } + cancelPossibleVeryLongPressReboot(); } private void cancelPendingBackKeyAction() { @@ -4901,6 +4913,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void schedulePossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout); + } + + private void cancelPossibleVeryLongPressReboot() { + mHandler.removeCallbacks(mPossibleVeryLongPressReboot); + } + // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 94cc6502a12d..9a5bd1379717 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -22,6 +22,7 @@ import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY; import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.END_OF_DAY; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; @@ -66,7 +67,7 @@ public class IntervalStats { public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>(); public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>(); public Configuration activeConfiguration; - public EventList events; + public EventList events = new EventList(); // A string cache. This is important as when we're parsing XML files, we don't want to // keep hundreds of strings that have the same contents. We will read the string @@ -112,6 +113,9 @@ public class IntervalStats { } + public IntervalStats() { + } + /** * Gets the UsageStats object for the given package, or creates one and adds it internally. */ @@ -253,6 +257,7 @@ public class IntervalStats { case ROLLOVER_FOREGROUND_SERVICE: case CONTINUE_PREVIOUS_DAY: case CONTINUING_FOREGROUND_SERVICE: + case DEVICE_SHUTDOWN: return true; } return false; @@ -281,8 +286,9 @@ public class IntervalStats { @VisibleForTesting public void update(String packageName, String className, long timeStamp, int eventType, int instanceId) { - if (eventType == FLUSH_TO_DISK) { - // FLUSH_TO_DISK are sent to all packages. + if (eventType == DEVICE_SHUTDOWN + || eventType == FLUSH_TO_DISK) { + // DEVICE_SHUTDOWN and FLUSH_TO_DISK are sent to all packages. final int size = packageStats.size(); for (int i = 0; i < size; i++) { UsageStats usageStats = packageStats.valueAt(i); @@ -321,9 +327,6 @@ public class IntervalStats { */ @VisibleForTesting public void addEvent(Event event) { - if (events == null) { - events = new EventList(); - } // Cache common use strings event.mPackage = getCachedStringRef(event.mPackage); if (event.mClass != null) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f146370b01d7..76a3aa81530e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -18,6 +18,7 @@ package com.android.server.usage; import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION; import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION; import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; @@ -416,11 +417,30 @@ public class UsageStatsService extends SystemService implements */ void shutdown() { synchronized (mLock) { + mHandler.removeMessages(MSG_REPORT_EVENT); + Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + // orderly shutdown, the last event is DEVICE_SHUTDOWN. + reportEventToAllUserId(event); flushToDiskLocked(); } } /** + * After power button is pressed for 3.5 seconds + * (as defined in {@link com.android.internal.R.integer#config_veryLongPressTimeout}), + * report DEVICE_SHUTDOWN event and persist the database. If the power button is pressed for 10 + * seconds and the device is shutdown, the database is already persisted and we are not losing + * data. + * This method is called from PhoneWindowManager, do not synchronize on mLock otherwise + * PhoneWindowManager may be blocked. + */ + void prepareForPossibleShutdown() { + Event event = new Event(DEVICE_SHUTDOWN, SystemClock.elapsedRealtime()); + mHandler.obtainMessage(MSG_REPORT_EVENT_TO_ALL_USERID, event).sendToTarget(); + mHandler.sendEmptyMessage(MSG_FLUSH_TO_DISK); + } + + /** * Called by the Binder stub. */ void reportEvent(Event event, int userId) { @@ -1487,6 +1507,11 @@ public class UsageStatsService extends SystemService implements } @Override + public void prepareForPossibleShutdown() { + UsageStatsService.this.prepareForPossibleShutdown(); + } + + @Override public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) { mAppStandby.addListener(listener); listener.onParoleStateChanged(isAppIdleParoleOn()); diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 5128ae1f9130..2d1098c7cf5c 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -16,6 +16,7 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN; import static android.app.usage.UsageStatsManager.INTERVAL_BEST; import static android.app.usage.UsageStatsManager.INTERVAL_COUNT; import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; @@ -134,6 +135,18 @@ class UserUsageStatsService { updateRolloverDeadline(); } + // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp + // is last time UsageStatsDatabase is persisted to disk. + final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY]; + if (currentDailyStats != null) { + final int size = currentDailyStats.events.size(); + if (size == 0 || currentDailyStats.events.get(size - 1).mEventType != DEVICE_SHUTDOWN) { + // The last event in event list is not DEVICE_SHUTDOWN, then we insert one. + final Event event = new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved); + currentDailyStats.addEvent(event); + } + } + if (mDatabase.isNewUpdate()) { notifyNewUpdate(); } @@ -175,7 +188,9 @@ class UserUsageStatsService { // ACTIVITY_STOPPED. && event.mEventType != Event.ACTIVITY_DESTROYED // FLUSH_TO_DISK is a private event. - && event.mEventType != Event.FLUSH_TO_DISK) { + && event.mEventType != Event.FLUSH_TO_DISK + // DEVICE_SHUTDOWN is added to event list after reboot. + && event.mEventType != Event.DEVICE_SHUTDOWN) { currentDailyStats.addEvent(event); } @@ -393,10 +408,6 @@ class UserUsageStatsService { @Override public void combine(IntervalStats stats, boolean mutable, List<Event> accumulatedResult) { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -434,10 +445,6 @@ class UserUsageStatsService { names.add(packageName); final List<Event> results = queryStats(INTERVAL_DAILY, beginTime, endTime, (stats, mutable, accumulatedResult) -> { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -696,10 +703,6 @@ class UserUsageStatsService { @Override public void combine(IntervalStats stats, boolean mutable, List<Event> accumulatedResult) { - if (stats.events == null) { - return; - } - final int startIndex = stats.events.firstIndexOnOrAfter(beginTime); final int size = stats.events.size(); for (int i = startIndex; i < size; i++) { @@ -925,10 +928,12 @@ class UserUsageStatsService { return "SCREEN_INTERACTIVE"; case Event.SCREEN_NON_INTERACTIVE: return "SCREEN_NON_INTERACTIVE"; - case UsageEvents.Event.KEYGUARD_SHOWN: + case Event.KEYGUARD_SHOWN: return "KEYGUARD_SHOWN"; - case UsageEvents.Event.KEYGUARD_HIDDEN: + case Event.KEYGUARD_HIDDEN: return "KEYGUARD_HIDDEN"; + case Event.DEVICE_SHUTDOWN: + return "DEVICE_SHUTDOWN"; default: return "UNKNOWN_TYPE_" + eventType; } |