summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/OWNERS1
-rw-r--r--core/java/android/view/FrameMetrics.java2
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/proto/OWNERS1
-rw-r--r--core/proto/android/server/apphibernationservice.proto42
-rw-r--r--core/tests/coretests/assets/fonts/OWNERS1
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/OWNERS1
-rw-r--r--core/tests/coretests/res/font/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java3
-rw-r--r--native/android/OWNERS1
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml22
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml13
-rw-r--r--packages/SystemUI/res/values/attrs.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml8
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt118
-rw-r--r--services/core/java/com/android/server/apphibernation/AppHibernationService.java284
-rw-r--r--services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java78
-rw-r--r--services/core/java/com/android/server/apphibernation/GlobalLevelState.java25
-rw-r--r--services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java162
-rw-r--r--services/core/java/com/android/server/apphibernation/ProtoReadWriter.java42
-rw-r--r--services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java78
-rw-r--r--services/core/java/com/android/server/apphibernation/UserLevelState.java25
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java20
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java14
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java8
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java8
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsLogger.java2
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java2
-rw-r--r--services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java236
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java2
39 files changed, 1189 insertions, 114 deletions
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 000000000000..18486af9d12c
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index c2566cc78bb0..5937499744b8 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -149,7 +149,7 @@ public final class FrameMetrics {
* <p>
* The time value that was used in all the vsync listeners and drawing for
* the frame (Choreographer frame callbacks, animations,
- * {@link View#getDrawingTime()}, etc…)
+ * {@link View#getDrawingTime()}, etc.)
* </p>
*/
public static final int VSYNC_TIMESTAMP = 11;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 92fe72146999..d196d4af2e42 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -162,7 +162,7 @@ public final class Zygote {
* GWP-ASan is activated unconditionally (but still, only a small subset of
* allocations is protected).
*/
- public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
+ public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22;
/**
* Enable automatic zero-initialization of native heap memory allocations.
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 748b4b4f5743..99fd21592411 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@ ogunwale@google.com
jjaggi@google.com
roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
kchyn@google.com
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
new file mode 100644
index 000000000000..d341c4b2f0a8
--- /dev/null
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+syntax = "proto2";
+package com.android.server.apphibernation;
+
+option java_multiple_files = true;
+
+// Proto for hibernation states for all packages for a user.
+message UserLevelHibernationStatesProto {
+ repeated UserLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.UserLevelState.
+message UserLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+}
+
+// Proto for global hibernation states for all packages.
+message GlobalLevelHibernationStatesProto {
+ repeated GlobalLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.GlobalLevelState
+message GlobalLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+} \ No newline at end of file
diff --git a/core/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS
new file mode 100644
index 000000000000..b0e0b9deaddb
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/OWNERS b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
new file mode 100644
index 000000000000..b0e0b9deaddb
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/res/font/OWNERS b/core/tests/coretests/res/font/OWNERS
new file mode 100644
index 000000000000..b0e0b9deaddb
--- /dev/null
+++ b/core/tests/coretests/res/font/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 7538c8b7ffad..2b53257e2774 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -771,7 +771,8 @@ public class BubbleController {
// if the bubble is already active, there's no need to push it to overflow
return;
}
- bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
+ bubble.inflate(
+ (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
});
return null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 9d196ba32f8a..53b75373a647 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -542,7 +542,8 @@ public class BubbleData {
void overflowBubble(@DismissReason int reason, Bubble bubble) {
if (bubble.getPendingIntentCanceled()
|| !(reason == Bubbles.DISMISS_AGED
- || reason == Bubbles.DISMISS_USER_GESTURE)) {
+ || reason == Bubbles.DISMISS_USER_GESTURE
+ || reason == Bubbles.DISMISS_RELOAD_FROM_DISK)) {
return;
}
if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
index 3361c4ce11da..c88a58be1461 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java
@@ -61,7 +61,10 @@ public class BubbleLogger {
BUBBLE_OVERFLOW_REMOVE_BLOCKED(490),
@UiEvent(doc = "User selected the overflow.")
- BUBBLE_OVERFLOW_SELECTED(600);
+ BUBBLE_OVERFLOW_SELECTED(600),
+
+ @UiEvent(doc = "Restore bubble to overflow after phone reboot.")
+ BUBBLE_OVERFLOW_RECOVER(691);
private final int mId;
@@ -112,6 +115,8 @@ public class BubbleLogger {
log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
} else if (r == Bubbles.DISMISS_USER_GESTURE) {
log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+ } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) {
+ log(b, Event.BUBBLE_OVERFLOW_RECOVER);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 6102147e4b53..6a1026bb24fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -54,7 +54,7 @@ public interface Bubbles {
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP})
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {}
@@ -72,6 +72,7 @@ public interface Bubbles {
int DISMISS_SHORTCUT_REMOVED = 12;
int DISMISS_PACKAGE_REMOVED = 13;
int DISMISS_NO_BUBBLE_UP = 14;
+ int DISMISS_RELOAD_FROM_DISK = 15;
/**
* @return {@code true} if there is a bubble associated with the provided key and if its
diff --git a/native/android/OWNERS b/native/android/OWNERS
index ac5a89527ef0..d414ed4cd5e2 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,3 +1,4 @@
per-file libandroid_net.map.txt, net.c = set noparent
per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
+per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
index 108591beb05a..d097472471b0 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
@@ -15,22 +15,24 @@
~ limitations under the License.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack">
+ android:paddingMode="stack" >
<item android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
- <layer-list >
+ <layer-list>
<item>
<shape
android:tint="?android:attr/colorControlActivated"
android:alpha="?android:attr/disabledAlpha">
- <size android:height="48dp" />
+ <size android:height="@dimen/rounded_slider_height" />
<solid android:color="@color/white_disabled" />
- <corners android:radius="24dp" />
+ <corners android:radius="@dimen/rounded_slider_corner_radius" />
</shape>
</item>
<item
- android:gravity="center_vertical|start"
- android:start="32dp">
+ android:gravity="center_vertical|left"
+ android:height="@dimen/rounded_slider_icon_size"
+ android:width="@dimen/rounded_slider_icon_size"
+ android:left="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorControlActivated" />
@@ -39,10 +41,8 @@
</item>
<item android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
- <clip
- android:drawable="@drawable/brightness_progress_full_drawable"
- android:clipOrientation="horizontal"
- android:gravity="left"
- />
+ <com.android.systemui.util.RoundedCornerProgressDrawable
+ android:drawable="@drawable/brightness_progress_full_drawable"
+ />
</item>
</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index b5def5ebf539..41140a7a8c85 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -15,18 +15,21 @@
~ limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true">
<item android:id="@+id/slider_foreground">
<shape>
- <size android:height="48dp" />
+ <size android:height="@dimen/rounded_slider_height" />
<solid android:color="?android:attr/colorControlActivated" />
- <corners android:radius="24dp"/>
+ <corners android:radius="@dimen/rounded_slider_corner_radius"/>
</shape>
</item>
<item
android:id="@+id/slider_icon"
- android:gravity="center_vertical|start"
- android:start="32dp">
+ android:gravity="center_vertical|right"
+ android:height="@dimen/rounded_slider_icon_size"
+ android:width="@dimen/rounded_slider_icon_size"
+ android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
android:tint="?android:attr/colorBackground"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e2fe223f591b..6c55fb62e638 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -177,5 +177,9 @@
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
</declare-styleable>
+
+ <declare-styleable name="RoundedCornerProgressDrawable">
+ <attr name="android:drawable" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1fac96bb181d..d92f4ea65390 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1333,4 +1333,12 @@
<dimen name="people_space_widget_radius">24dp</dimen>
<dimen name="people_space_widget_round_radius">100dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
+
+ <dimen name="rounded_slider_height">48dp</dimen>
+ <!-- rounded_slider_height / 2 -->
+ <dimen name="rounded_slider_corner_radius">24dp</dimen>
+ <!-- rounded_slider_height / 2 -->
+ <dimen name="rounded_slider_icon_size">24dp</dimen>
+ <!-- rounded_slider_icon_size / 2 -->
+ <dimen name="rounded_slider_icon_inset">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 3bc5ebf8c64e..5c650ad3982e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -325,8 +325,6 @@ public class ScreenshotView extends FrameLayout implements
Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
}
- Rect previewBounds = new Rect();
- mScreenshotPreview.getBoundsOnScreen(previewBounds);
Rect targetPosition = new Rect();
mScreenshotPreview.getHitRect(targetPosition);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
index 8dcc8b46f024..3c7d78c928fa 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessControllerSettings.java
@@ -18,7 +18,6 @@ package com.android.systemui.settings.brightness;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
@@ -30,25 +29,21 @@ public class BrightnessControllerSettings {
private static final String THICK_BRIGHTNESS_SLIDER = "sysui_thick_brightness";
private final FeatureFlags mFeatureFlags;
- private final boolean mUseThickSlider;
- private final boolean mUseMirrorOnThickSlider;
@Inject
- public BrightnessControllerSettings(SecureSettings settings, FeatureFlags featureFlags) {
+ public BrightnessControllerSettings(FeatureFlags featureFlags) {
mFeatureFlags = featureFlags;
- mUseThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 0;
- mUseMirrorOnThickSlider = settings.getInt(THICK_BRIGHTNESS_SLIDER, 0) != 2;
}
// Changing this setting between zero and non-zero may crash systemui down the line. Better to
// restart systemui after changing it.
/** */
boolean useThickSlider() {
- return mUseThickSlider && mFeatureFlags.useNewBrightnessSlider();
+ return mFeatureFlags.useNewBrightnessSlider();
}
/** */
boolean useMirrorOnThickSlider() {
- return !useThickSlider() || (useThickSlider() && mUseMirrorOnThickSlider);
+ return !useThickSlider();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 53ff1dfd277b..a6aec3b7b1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -17,7 +17,6 @@
package com.android.systemui.settings.brightness;
import android.content.Context;
-import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.view.LayoutInflater;
@@ -33,6 +32,7 @@ import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -292,8 +292,8 @@ public class BrightnessSlider
if (b.getProgressDrawable() instanceof LayerDrawable) {
Drawable progress = ((LayerDrawable) b.getProgressDrawable())
.findDrawableByLayerId(com.android.internal.R.id.progress);
- if (progress instanceof ClipDrawable) {
- Drawable inner = ((ClipDrawable) progress).getDrawable();
+ if (progress instanceof RoundedCornerProgressDrawable) {
+ Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable();
if (inner instanceof LayerDrawable) {
return (LayerDrawable) inner;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
new file mode 100644
index 000000000000..1af2c9f46373
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 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.systemui.util
+
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.util.AttributeSet
+import com.android.systemui.R
+import org.xmlpull.v1.XmlPullParser
+
+/**
+ * [DrawableWrapper] to use in the progress of a slider.
+ *
+ * This drawable is used to change the bounds of the enclosed drawable depending on the level to
+ * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the
+ * edges is maintained.
+ *
+ * Meant to be used with a rounded ends background, it will also prevent deformation when the slider
+ * is meant to be smaller than the rounded corner. The background should have rounded corners that
+ * are half of the height.
+ */
+class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) {
+
+ constructor() : this(null)
+
+ companion object {
+ private const val MAX_LEVEL = 10000 // Taken from Drawable
+ }
+
+ private var clipPath: Path = Path()
+
+ init {
+ setClipPath(Rect())
+ }
+
+ override fun inflate(
+ r: Resources,
+ parser: XmlPullParser,
+ attrs: AttributeSet,
+ theme: Resources.Theme?
+ ) {
+ val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable)
+
+ // Inflation will advance the XmlPullParser and AttributeSet.
+ super.inflate(r, parser, attrs, theme)
+
+ updateStateFromTypedArray(a)
+ if (drawable == null) {
+ throw IllegalStateException("${this::class.java.simpleName} needs a drawable")
+ }
+ a.recycle()
+ }
+
+ override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {
+ onLevelChange(level)
+ return super.onLayoutDirectionChanged(layoutDirection)
+ }
+
+ private fun updateStateFromTypedArray(a: TypedArray) {
+ if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) {
+ setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable))
+ }
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ setClipPath(bounds)
+ super.onBoundsChange(bounds)
+ onLevelChange(level)
+ }
+
+ private fun setClipPath(bounds: Rect) {
+ clipPath.reset()
+ clipPath.addRoundRect(
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ bounds.right.toFloat(),
+ bounds.bottom.toFloat(),
+ bounds.height().toFloat() / 2,
+ bounds.height().toFloat() / 2,
+ Path.Direction.CW
+ )
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ val db = drawable?.bounds!!
+ val width = bounds.width() * level / MAX_LEVEL
+ // Extra space on the left to keep the rounded shape on the right end
+ val leftBound = bounds.left - bounds.height()
+ drawable?.setBounds(leftBound, db.top, bounds.left + width, db.bottom)
+ return super.onLevelChange(level)
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.save()
+ canvas.clipPath(clipPath)
+ super.draw(canvas)
+ canvas.restore()
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index fc48e2df39e8..e97f0b47380a 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -20,7 +20,7 @@ import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
import static android.content.Intent.EXTRA_REPLACING;
-import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import android.annotation.NonNull;
@@ -34,7 +34,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -52,10 +54,14 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
+import java.io.File;
import java.io.FileDescriptor;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
/**
* System service that manages app hibernation state, a state apps can enter that means they are
@@ -64,6 +70,11 @@ import java.util.Set;
*/
public final class AppHibernationService extends SystemService {
private static final String TAG = "AppHibernationService";
+ private static final int PACKAGE_MATCH_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS;
/**
* Lock for accessing any in-memory hibernation state
@@ -74,9 +85,13 @@ public final class AppHibernationService extends SystemService {
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
@GuardedBy("mLock")
- private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+ private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
+ private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
+ new SparseArray<>();
@GuardedBy("mLock")
- private final Set<String> mGloballyHibernatedPackages = new ArraySet<>();
+ private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
+ private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
+ private final Injector mInjector;
/**
* Initializes the system service.
@@ -88,19 +103,18 @@ public final class AppHibernationService extends SystemService {
* @param context The system server context.
*/
public AppHibernationService(@NonNull Context context) {
- this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
- ActivityManager.getService(),
- context.getSystemService(UserManager.class));
+ this(new InjectorImpl(context));
}
@VisibleForTesting
- AppHibernationService(@NonNull Context context, IPackageManager packageManager,
- IActivityManager activityManager, UserManager userManager) {
- super(context);
- mContext = context;
- mIPackageManager = packageManager;
- mIActivityManager = activityManager;
- mUserManager = userManager;
+ AppHibernationService(@NonNull Injector injector) {
+ super(injector.getContext());
+ mContext = injector.getContext();
+ mIPackageManager = injector.getPackageManager();
+ mIActivityManager = injector.getActivityManager();
+ mUserManager = injector.getUserManager();
+ mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+ mInjector = injector;
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
@@ -116,6 +130,17 @@ public final class AppHibernationService extends SystemService {
publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ List<GlobalLevelState> states =
+ mGlobalLevelHibernationDiskStore.readHibernationStates();
+ synchronized (mLock) {
+ initializeGlobalHibernationStates(states);
+ }
+ }
+ }
+
/**
* Whether a package is hibernating for a given user.
*
@@ -131,8 +156,8 @@ public final class AppHibernationService extends SystemService {
return false;
}
synchronized (mLock) {
- final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
- final UserPackageState pkgState = packageStates.get(packageName);
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed for user %s",
@@ -150,7 +175,12 @@ public final class AppHibernationService extends SystemService {
*/
boolean isHibernatingGlobally(String packageName) {
synchronized (mLock) {
- return mGloballyHibernatedPackages.contains(packageName);
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed", packageName));
+ }
+ return state.hibernated;
}
}
@@ -169,8 +199,8 @@ public final class AppHibernationService extends SystemService {
return;
}
synchronized (mLock) {
- Map<String, UserPackageState> packageStates = mUserStates.get(userId);
- UserPackageState pkgState = packageStates.get(packageName);
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed for user %s",
@@ -182,10 +212,12 @@ public final class AppHibernationService extends SystemService {
}
if (isHibernating) {
- hibernatePackageForUserL(packageName, userId, pkgState);
+ hibernatePackageForUser(packageName, userId, pkgState);
} else {
- unhibernatePackageForUserL(packageName, userId, pkgState);
+ unhibernatePackageForUser(packageName, userId, pkgState);
}
+ List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
+ mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
}
}
@@ -197,25 +229,32 @@ public final class AppHibernationService extends SystemService {
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
- if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for any user", packageName));
+ }
+ if (state.hibernated != isHibernating) {
if (isHibernating) {
- hibernatePackageGloballyL(packageName);
+ hibernatePackageGlobally(packageName, state);
} else {
- unhibernatePackageGloballyL(packageName);
+ unhibernatePackageGlobally(packageName, state);
}
+ List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values());
+ mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states);
}
}
}
/**
* Put an app into hibernation for a given user, allowing user-level optimizations to occur.
- * The caller should hold {@link #mLock}
*
* @param pkgState package hibernation state
*/
- private void hibernatePackageForUserL(@NonNull String packageName, int userId,
- @NonNull UserPackageState pkgState) {
+ @GuardedBy("mLock")
+ private void hibernatePackageForUser(@NonNull String packageName, int userId,
+ @NonNull UserLevelState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
@@ -233,12 +272,13 @@ public final class AppHibernationService extends SystemService {
}
/**
- * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}.
+ * Remove a package from hibernation for a given user.
*
* @param pkgState package hibernation state
*/
- private void unhibernatePackageForUserL(@NonNull String packageName, int userId,
- UserPackageState pkgState) {
+ @GuardedBy("mLock")
+ private void unhibernatePackageForUser(@NonNull String packageName, int userId,
+ UserLevelState pkgState) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
@@ -255,64 +295,140 @@ public final class AppHibernationService extends SystemService {
/**
* Put a package into global hibernation, optimizing its storage at a package / APK level.
- * The caller should hold {@link #mLock}.
*/
- private void hibernatePackageGloballyL(@NonNull String packageName) {
+ @GuardedBy("mLock")
+ private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
// TODO(175830194): Delete vdex/odex when DexManager API is built out
- mGloballyHibernatedPackages.add(packageName);
+ state.hibernated = true;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
- * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}.
+ * Unhibernate a package from global hibernation.
*/
- private void unhibernatePackageGloballyL(@NonNull String packageName) {
+ @GuardedBy("mLock")
+ private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
- mGloballyHibernatedPackages.remove(packageName);
+ state.hibernated = false;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
- * Populates {@link #mUserStates} with the users installed packages. The caller should hold
- * {@link #mLock}.
+ * Initializes in-memory store of user-level hibernation states for the given user
*
* @param userId user id to add installed packages for
+ * @param diskStates states pulled from disk, if available
+ */
+ @GuardedBy("mLock")
+ private void initializeUserHibernationStates(int userId,
+ @Nullable List<UserLevelState> diskStates) {
+ List<PackageInfo> packages;
+ try {
+ packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available", e);
+ }
+
+ Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
+
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ UserLevelState state = new UserLevelState();
+ state.packageName = packageName;
+ userLevelStates.put(packageName, state);
+ }
+
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ String packageName = diskStates.get(i).packageName;
+ if (!installedPackages.contains(packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s user %d. Maybe"
+ + "the package was uninstalled? ", packageName, userId));
+ continue;
+ }
+ userLevelStates.put(packageName, diskStates.get(i));
+ }
+ }
+ mUserStates.put(userId, userLevelStates);
+ }
+
+ /**
+ * Initialize in-memory store of global level hibernation states.
+ *
+ * @param diskStates global level hibernation states pulled from disk, if available
*/
- private void addUserPackageStatesL(int userId) {
- Map<String, UserPackageState> packages = new ArrayMap<>();
- List<PackageInfo> packageList;
+ @GuardedBy("mLock")
+ private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
+ List<PackageInfo> packages;
try {
- packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+ packages = mIPackageManager.getInstalledPackages(
+ PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager not available.", e);
+ throw new IllegalStateException("Package manager not available", e);
}
- for (int i = 0, size = packageList.size(); i < size; i++) {
- packages.put(packageList.get(i).packageName, new UserPackageState());
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ GlobalLevelState state = new GlobalLevelState();
+ state.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, state);
+ }
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ GlobalLevelState state = diskStates.get(i);
+ if (!installedPackages.contains(state.packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s. Maybe the "
+ + "package was uninstalled? ", state.packageName));
+ continue;
+ }
+ mGlobalHibernationStates.put(state.packageName, state);
+ }
}
- mUserStates.put(userId, packages);
}
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
- // TODO: Pull from persistent disk storage. For now, just make from scratch.
+ int userId = user.getUserIdentifier();
+ HibernationStateDiskStore<UserLevelState> diskStore =
+ mInjector.getUserLevelDiskStore(userId);
+ mUserDiskStores.put(userId, diskStore);
+ List<UserLevelState> storedStates = diskStore.readHibernationStates();
synchronized (mLock) {
- addUserPackageStatesL(user.getUserIdentifier());
+ initializeUserHibernationStates(userId, storedStates);
}
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
+ int userId = user.getUserIdentifier();
+ // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
synchronized (mLock) {
- // TODO: Flush to disk when persistence is implemented
- mUserStates.remove(user.getUserIdentifier());
+ mUserDiskStores.remove(userId);
+ mUserStates.remove(userId);
}
}
private void onPackageAdded(@NonNull String packageName, int userId) {
synchronized (mLock) {
- mUserStates.get(userId).put(packageName, new UserPackageState());
+ UserLevelState userState = new UserLevelState();
+ userState.packageName = packageName;
+ mUserStates.get(userId).put(packageName, userState);
+ if (!mGlobalHibernationStates.containsKey(packageName)) {
+ GlobalLevelState globalState = new GlobalLevelState();
+ globalState.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, globalState);
+ }
}
}
@@ -324,7 +440,7 @@ public final class AppHibernationService extends SystemService {
private void onPackageRemovedForAllUsers(@NonNull String packageName) {
synchronized (mLock) {
- mGloballyHibernatedPackages.remove(packageName);
+ mGlobalHibernationStates.remove(packageName);
}
}
@@ -425,10 +541,66 @@ public final class AppHibernationService extends SystemService {
}
/**
- * Data class that contains hibernation state info of a package for a user.
+ * Dependency injector for {@link #AppHibernationService)}.
*/
- private static final class UserPackageState {
- public boolean hibernated;
- // TODO: Track whether hibernation is exempted by the user
+ interface Injector {
+ Context getContext();
+
+ IPackageManager getPackageManager();
+
+ IActivityManager getActivityManager();
+
+ UserManager getUserManager();
+
+ HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
+
+ HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
+ }
+
+ private static final class InjectorImpl implements Injector {
+ private static final String HIBERNATION_DIR_NAME = "hibernation";
+ private final Context mContext;
+ private final ScheduledExecutorService mScheduledExecutorService;
+ private final UserLevelHibernationProto mUserLevelHibernationProto;
+
+ InjectorImpl(Context context) {
+ mContext = context;
+ mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ mUserLevelHibernationProto = new UserLevelHibernationProto();
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return ActivityManager.getService();
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, mUserLevelHibernationProto, mScheduledExecutorService);
+ }
}
}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
new file mode 100644
index 000000000000..79e995b038fa
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link GlobalLevelState} hiberation states.
+ */
+final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLevelState>> {
+ private static final String TAG = "GlobalLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<GlobalLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ GlobalLevelState state = data.get(i);
+ stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<GlobalLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<GlobalLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) GlobalLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ GlobalLevelState state = new GlobalLevelState();
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) GlobalLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(GlobalLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) GlobalLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
new file mode 100644
index 000000000000..4f756756c2ab
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+/**
+ * Data class that contains global hibernation state for a package.
+ */
+final class GlobalLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
new file mode 100644
index 000000000000..c83659d2ff56
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Disk store utility class for hibernation states.
+ *
+ * @param <T> the type of hibernation state data
+ */
+class HibernationStateDiskStore<T> {
+ private static final String TAG = "HibernationStateDiskStore";
+
+ // Time to wait before actually writing. Saves extra writes if data changes come in batches.
+ private static final long DISK_WRITE_DELAY = 1L * DateUtils.MINUTE_IN_MILLIS;
+ private static final String STATES_FILE_NAME = "states";
+
+ private final File mHibernationFile;
+ private final ScheduledExecutorService mExecutorService;
+ private final ProtoReadWriter<List<T>> mProtoReadWriter;
+ private List<T> mScheduledStatesToWrite = new ArrayList<>();
+ private ScheduledFuture<?> mFuture;
+
+ /**
+ * Initialize a disk store for hibernation states in the given directory.
+ *
+ * @param hibernationDir directory to write/read states file
+ * @param readWriter writer/reader of states proto
+ * @param executorService scheduled executor for writing data
+ */
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService) {
+ this(hibernationDir, readWriter, executorService, STATES_FILE_NAME);
+ }
+
+ @VisibleForTesting
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService,
+ @NonNull String fileName) {
+ mHibernationFile = new File(hibernationDir, fileName);
+ mExecutorService = executorService;
+ mProtoReadWriter = readWriter;
+ }
+
+ /**
+ * Schedule a full write of all the hibernation states to the file on disk. Does not run
+ * immediately and subsequent writes override previous ones.
+ *
+ * @param hibernationStates list of hibernation states to write to disk
+ */
+ void scheduleWriteHibernationStates(@NonNull List<T> hibernationStates) {
+ synchronized (this) {
+ mScheduledStatesToWrite = hibernationStates;
+ if (mExecutorService.isShutdown()) {
+ Slog.e(TAG, "Scheduled executor service is shut down.");
+ return;
+ }
+
+ // Already have write scheduled
+ if (mFuture != null) {
+ Slog.i(TAG, "Write already scheduled. Skipping schedule.");
+ return;
+ }
+
+ mFuture = mExecutorService.schedule(this::writeHibernationStates, DISK_WRITE_DELAY,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Read hibernation states from disk.
+ *
+ * @return the parsed list of hibernation states, null if file does not exist
+ */
+ @Nullable
+ List<T> readHibernationStates() {
+ synchronized (this) {
+ if (!mHibernationFile.exists()) {
+ Slog.i(TAG, "No hibernation file on disk for file " + mHibernationFile.getPath());
+ return null;
+ }
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ try {
+ FileInputStream inputStream = atomicFile.openRead();
+ ProtoInputStream protoInputStream = new ProtoInputStream(inputStream);
+ return mProtoReadWriter.readFromProto(protoInputStream);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read states protobuf.", e);
+ return null;
+ }
+ }
+ }
+
+ @WorkerThread
+ private void writeHibernationStates() {
+ synchronized (this) {
+ writeStateProto(mScheduledStatesToWrite);
+ mScheduledStatesToWrite.clear();
+ mFuture = null;
+ }
+ }
+
+ @WorkerThread
+ private void writeStateProto(List<T> states) {
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ FileOutputStream fileOutputStream;
+ try {
+ fileOutputStream = atomicFile.startWrite();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to start write to states protobuf.", e);
+ return;
+ }
+
+ try {
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+ mProtoReadWriter.writeToProto(protoOutputStream, states);
+ protoOutputStream.flush();
+ atomicFile.finishWrite(fileOutputStream);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to finish write to states protobuf.", e);
+ atomicFile.failWrite(fileOutputStream);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
new file mode 100644
index 000000000000..0cbc09a7a99d
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+/**
+ * Proto utility that reads and writes proto for some data.
+ *
+ * @param <T> data that can be written and read from a proto
+ */
+interface ProtoReadWriter<T> {
+
+ /**
+ * Write data to a proto stream
+ */
+ void writeToProto(@NonNull ProtoOutputStream stream, @NonNull T data);
+
+ /**
+ * Parse data from the proto stream and return
+ */
+ @Nullable T readFromProto(@NonNull ProtoInputStream stream) throws IOException;
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
new file mode 100644
index 000000000000..a24c4c575975
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link UserLevelState} hiberation states.
+ */
+final class UserLevelHibernationProto implements ProtoReadWriter<List<UserLevelState>> {
+ private static final String TAG = "UserLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<UserLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ UserLevelState state = data.get(i);
+ stream.write(UserLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(UserLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<UserLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<UserLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) UserLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ UserLevelState state = new UserLevelState();
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) UserLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(UserLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) UserLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(UserLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
new file mode 100644
index 000000000000..c66dad87c891
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+/**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+final class UserLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f07da8f0236b..10fe1e1d0684 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2796,6 +2796,8 @@ public class AppOpsService extends IAppOpsService.Stub {
if (callback == null) {
return;
}
+ final boolean mayWatchPackageName =
+ packageName != null && !filterAppAccessUnlocked(packageName);
synchronized (this) {
int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
@@ -2824,7 +2826,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
cbs.add(cb);
}
- if (packageName != null) {
+ if (mayWatchPackageName) {
ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName);
if (cbs == null) {
cbs = new ArraySet<>();
@@ -3008,13 +3010,27 @@ public class AppOpsService extends IAppOpsService.Stub {
Objects.requireNonNull(packageName);
try {
verifyAndGetBypass(uid, packageName, null);
-
+ if (filterAppAccessUnlocked(packageName)) {
+ return AppOpsManager.MODE_ERRORED;
+ }
return AppOpsManager.MODE_ALLOWED;
} catch (SecurityException ignored) {
return AppOpsManager.MODE_ERRORED;
}
}
+ /**
+ * This method will check with PackageManager to determine if the package provided should
+ * be visible to the {@link Binder#getCallingUid()}.
+ *
+ * NOTE: This must not be called while synchronized on {@code this} to avoid dead locks
+ */
+ private boolean filterAppAccessUnlocked(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ return LocalServices.getService(PackageManagerInternal.class)
+ .filterAppAccess(packageName, callingUid, UserHandle.getUserId(callingUid));
+ }
+
@Override
public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
String proxiedAttributionTag, int proxyUid, String proxyPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 9869f779cc20..1135126d86fb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -113,6 +113,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
+ private boolean mIsUdfps = false;
private final int mSensorId;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -335,22 +336,22 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
}
final IBiometricsFingerprint daemon = getDaemon();
- boolean isUdfps = false;
+ mIsUdfps = false;
android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
daemon);
if (extension != null) {
try {
- isUdfps = extension.isUdfps(sensorId);
+ mIsUdfps = extension.isUdfps(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception while quering udfps", e);
- isUdfps = false;
+ mIsUdfps = false;
}
}
final @FingerprintSensorProperties.SensorType int sensorType =
- isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
- : FingerprintSensorProperties.TYPE_REAR;
+ mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+ : FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
final int maxEnrollmentsPerUser = mContext.getResources()
@@ -414,7 +415,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ mScheduler.getCurrentClient());
try {
- mDaemon = IBiometricsFingerprint.getService();
+ mDaemon = IBiometricsFingerprint.getService(true /* retry */);
} catch (java.util.NoSuchElementException e) {
// Service doesn't exist or cannot be opened.
Slog.w(TAG, "NoSuchElementException", e);
@@ -799,6 +800,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
JSONObject dump = new JSONObject();
try {
dump.put("service", TAG);
+ dump.put("isUdfps", mIsUdfps);
JSONArray sets = new JSONArray();
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 90368129e78e..2eabf4054d84 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -219,7 +219,13 @@ public final class FontManagerService extends IFontManager.Stub {
private void initialize() {
synchronized (mUpdatableFontDirLock) {
if (mUpdatableFontDir == null) {
- updateSerializedFontMap();
+ synchronized (mSerializedFontMapLock) {
+ try {
+ mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap());
+ } catch (IOException | ErrnoException e) {
+ mSerializedFontMap = null;
+ }
+ }
return;
}
if (mFontCrashDetector.hasCrashed()) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index eb9df756dd0e..9a91848475fe 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -120,7 +120,7 @@ public final class PowerStatsHALWrapper {
* @return List of EnergyMeasurement objects containing energy measurements for all
* available energy meters.
*/
- android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds);
+ android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds);
/**
* Returns boolean indicating if connection to power stats HAL was established.
@@ -235,13 +235,13 @@ public final class PowerStatsHALWrapper {
}
@Override
- public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null;
if (sVintfPowerStats != null) {
try {
energyMeasurementHAL =
- sVintfPowerStats.get().readEnergyMeters(channelIds);
+ sVintfPowerStats.get().readEnergyMeter(channelIds);
} catch (RemoteException e) {
if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL");
}
@@ -311,7 +311,7 @@ public final class PowerStatsHALWrapper {
}
@Override
- public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
return nativeReadEnergyMeters(channelIds);
}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 78a227ee170e..e117b0c835bf 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -69,7 +69,7 @@ public final class PowerStatsLogger extends Handler {
// Log power meter data.
EnergyMeasurement[] energyMeasurements =
- mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+ mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
mPowerStatsMeterStorage.write(
EnergyMeasurementUtils.getProtoBytes(energyMeasurements));
if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements);
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index be5c2e133486..ea41980c02b1 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -291,6 +291,6 @@ public class PowerStatsService extends SystemService {
private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future,
int[] channelIds) {
- future.complete(getPowerStatsHal().readEnergyMeters(channelIds));
+ future.complete(getPowerStatsHal().readEnergyMeter(channelIds));
}
}
diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
index f8b9601bfd62..7c6999acc666 100644
--- a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
+++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java
@@ -70,7 +70,7 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall
}
private int pullOnDevicePowerMeasurement(int atomTag, List<StatsEvent> events) {
- EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(new int[0]);
+ EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]);
if (energyMeasurements == null) {
return StatsManager.PULL_SKIP;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bbf6c7616d46..1ece9870f0c9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -846,6 +846,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
@@ -862,8 +864,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 6777e1a5bc35..1328b91d03f9 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,12 +16,15 @@
package com.android.server.apphibernation;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
@@ -47,6 +50,7 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -75,16 +79,19 @@ public final class AppHibernationServiceTest {
private IActivityManager mIActivityManager;
@Mock
private UserManager mUserManager;
+ @Mock
+ private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
@Captor
private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
@Before
public void setUp() throws RemoteException {
+ // Share class loader to allow access to package-private classes
+ System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
- mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
- mIActivityManager, mUserManager);
+ mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
@@ -94,6 +101,12 @@ public final class AppHibernationServiceTest {
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any());
+ List<PackageInfo> packages = new ArrayList<>();
+ packages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
+ intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
+ mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
UserInfo userInfo = addUser(USER_ID_1);
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
@@ -152,7 +165,7 @@ public final class AppHibernationServiceTest {
}
@Test
- public void testSetHibernatingGlobally_packageIsHibernatingGlobally() {
+ public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException {
// WHEN we hibernate a package
mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
@@ -178,7 +191,7 @@ public final class AppHibernationServiceTest {
userPackages.add(makePackageInfo(pkgName));
}
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
- .getInstalledPackages(anyInt(), eq(userId));
+ .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
return userInfo;
}
@@ -187,4 +200,42 @@ public final class AppHibernationServiceTest {
pkg.packageName = packageName;
return pkg;
}
+
+ private class MockInjector implements AppHibernationService.Injector {
+ private final Context mContext;
+
+ MockInjector(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return mIActivityManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return mIPackageManager;
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
new file mode 100644
index 000000000000..59f3c35f2137
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.FileUtils;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+@SmallTest
+public class HibernationStateDiskStoreTest {
+ private static final String STATES_FILE_NAME = "states";
+ private final MockScheduledExecutorService mMockScheduledExecutorService =
+ new MockScheduledExecutorService();
+
+ private File mFile;
+ private HibernationStateDiskStore<String> mHibernationStateDiskStore;
+
+
+ @Before
+ public void setUp() {
+ mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+ mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile,
+ new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME);
+ }
+
+ @After
+ public void tearDown() {
+ FileUtils.deleteContentsAndDir(mFile);
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() {
+ // GIVEN some data to be written
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+
+ // WHEN the data is written
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the read data is equal to what was written
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() {
+ // GIVEN store has some data it is scheduled to write
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(
+ new ArrayList<>(Arrays.asList("C", "D")));
+
+ // WHEN a write is scheduled with new data
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the written data is the last scheduled data
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ /**
+ * Mock proto read / writer that just writes and reads a list of String data.
+ */
+ private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> {
+ private static final long FIELD_ID = 1;
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<String> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ stream.write(FIELD_ID, data.get(i));
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<String> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ ArrayList<String> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ list.add(stream.readString(FIELD_ID));
+ }
+ return list;
+ }
+ }
+
+ /**
+ * Mock scheduled executor service that has minimum implementation and can synchronously
+ * execute scheduled tasks.
+ */
+ private final class MockScheduledExecutorService implements ScheduledExecutorService {
+
+ Runnable mScheduledRunnable = null;
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ mScheduledRunnable = command;
+ return Mockito.mock(ScheduledFuture.class);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
+ long period, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ throw new UnsupportedOperationException();
+ }
+
+ void executeScheduledTask() {
+ mScheduledRunnable.run();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 84b690f01b02..53ddb288b4e7 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -221,7 +221,7 @@ public class PowerStatsServiceTest {
}
@Override
- public EnergyMeasurement[] readEnergyMeters(int[] channelIds) {
+ public EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT];
for (int i = 0; i < energyMeasurementList.length; i++) {
energyMeasurementList[i] = new EnergyMeasurement();