summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/app/ActivityManagerInternal.java6
-rw-r--r--core/java/android/app/usage/EventList.java17
-rw-r--r--core/java/android/app/usage/UsageEvents.java10
-rw-r--r--core/java/android/app/usage/UsageStats.java9
-rw-r--r--core/java/android/app/usage/UsageStatsManagerInternal.java6
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsTest.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java21
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java25
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java15
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java25
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java35
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;
}