Merge "Remove some more dead-code from old split impl" into rvc-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 23c1a23..5de6c2a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4859,6 +4859,11 @@
method public abstract String asyncImpl() default "";
}
+ public final class SurfaceControl implements android.os.Parcelable {
+ method public static long acquireFrameRateFlexibilityToken();
+ method public static void releaseFrameRateFlexibilityToken(long);
+ }
+
public class SurfaceControlViewHost {
method public void relayout(android.view.WindowManager.LayoutParams);
method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java
index a95bf3b..c5aff9d 100644
--- a/core/java/android/annotation/NonNull.java
+++ b/core/java/android/annotation/NonNull.java
@@ -30,8 +30,8 @@
* <p>
* This is a marker annotation and it has no specific attributes.
*
- * @paramDoc This value must never be {@code null}.
- * @returnDoc This value will never be {@code null}.
+ * @paramDoc This value cannot be {@code null}.
+ * @returnDoc This value cannot be {@code null}.
* @hide
*/
@Retention(SOURCE)
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a37c1cb..b5f9df7 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
@@ -216,6 +217,9 @@
private static native void nativeSetFrameRate(
long transactionObj, long nativeObject, float frameRate, int compatibility);
+ private static native long nativeAcquireFrameRateFlexibilityToken();
+ private static native void nativeReleaseFrameRateFlexibilityToken(long token);
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
/**
@@ -2868,4 +2872,24 @@
}
}
}
+
+ /**
+ * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
+ * frame rates. This is used by CTS tests to put the device in a consistent state. See
+ * ISurfaceComposer::acquireFrameRateFlexibilityToken().
+ * @hide
+ */
+ @TestApi
+ public static long acquireFrameRateFlexibilityToken() {
+ return nativeAcquireFrameRateFlexibilityToken();
+ }
+
+ /**
+ * Release a frame rate flexibility token.
+ * @hide
+ */
+ @TestApi
+ public static void releaseFrameRateFlexibilityToken(long token) {
+ nativeReleaseFrameRateFlexibilityToken(token);
+ }
}
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 71ccb59..849488d 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -484,21 +485,21 @@
super.onInitializeAccessibilityNodeInfo(info);
if (this.getOrientation() == HORIZONTAL) {
info.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1,
- getVisibleChildCount(), false,
+ getVisibleChildWithTextCount(), false,
AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
} else {
info.setCollectionInfo(
- AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildCount(),
+ AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildWithTextCount(),
1, false,
AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
}
}
- private int getVisibleChildCount() {
+ private int getVisibleChildWithTextCount() {
int count = 0;
for (int i = 0; i < getChildCount(); i++) {
if (this.getChildAt(i) instanceof RadioButton) {
- if (((RadioButton) this.getChildAt(i)).getVisibility() == VISIBLE) {
+ if (isVisibleWithText((RadioButton) this.getChildAt(i))) {
count++;
}
}
@@ -513,15 +514,19 @@
int index = 0;
for (int i = 0; i < getChildCount(); i++) {
if (this.getChildAt(i) instanceof RadioButton) {
- RadioButton radioButton = (RadioButton) this.getChildAt(i);
- if (radioButton == child) {
+ RadioButton button = (RadioButton) this.getChildAt(i);
+ if (button == child) {
return index;
}
- if (radioButton.getVisibility() == VISIBLE) {
+ if (isVisibleWithText(button)) {
index++;
}
}
}
return -1;
}
+
+ private boolean isVisibleWithText(RadioButton button) {
+ return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText());
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 5a979ac..2668420 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -40,7 +40,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
@@ -387,14 +386,17 @@
/** @hide */
public void setUnreadCount(int unreadCount) {
- mUnreadBadge.setVisibility(mIsCollapsed && unreadCount > 1 ? VISIBLE : GONE);
- CharSequence text = unreadCount >= 100
- ? getResources().getString(R.string.unread_convo_overflow, 99)
- : String.format(Locale.getDefault(), "%d", unreadCount);
- mUnreadBadge.setText(text);
- mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor));
- boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f;
- mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE);
+ boolean visible = mIsCollapsed && unreadCount > 1;
+ mUnreadBadge.setVisibility(visible ? VISIBLE : GONE);
+ if (visible) {
+ CharSequence text = unreadCount >= 100
+ ? getResources().getString(R.string.unread_convo_overflow, 99)
+ : String.format(Locale.getDefault(), "%d", unreadCount);
+ mUnreadBadge.setText(text);
+ mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor));
+ boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f;
+ mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE);
+ }
}
private void addRemoteInputHistoryToMessages(
@@ -537,37 +539,26 @@
}
private void updateImageMessages() {
- boolean displayExternalImage = false;
- ArraySet<View> newMessages = new ArraySet<>();
- if (mIsCollapsed) {
+ View newMessage = null;
+ if (mIsCollapsed && mGroups.size() > 0) {
- // When collapsed, we're displaying all image messages in a dedicated container
- // on the right of the layout instead of inline. Let's add all isolated images there
- int imageIndex = 0;
- for (int i = 0; i < mGroups.size(); i++) {
- MessagingGroup messagingGroup = mGroups.get(i);
- MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
- if (isolatedMessage != null) {
- newMessages.add(isolatedMessage.getView());
- displayExternalImage = true;
- if (imageIndex
- != mImageMessageContainer.indexOfChild(isolatedMessage.getView())) {
- mImageMessageContainer.removeView(isolatedMessage.getView());
- mImageMessageContainer.addView(isolatedMessage.getView(), imageIndex);
- }
- imageIndex++;
- }
+ // When collapsed, we're displaying the image message in a dedicated container
+ // on the right of the layout instead of inline. Let's add the isolated image there
+ MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1);
+ MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ newMessage = isolatedMessage.getView();
}
}
// Remove all messages that don't belong into the image layout
- for (int i = 0; i < mImageMessageContainer.getChildCount(); i++) {
- View child = mImageMessageContainer.getChildAt(i);
- if (!newMessages.contains(child)) {
- mImageMessageContainer.removeView(child);
- i--;
+ View previousMessage = mImageMessageContainer.getChildAt(0);
+ if (previousMessage != newMessage) {
+ mImageMessageContainer.removeView(previousMessage);
+ if (newMessage != null) {
+ mImageMessageContainer.addView(newMessage);
}
}
- mImageMessageContainer.setVisibility(displayExternalImage ? VISIBLE : GONE);
+ mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE);
}
private void bindFacePile() {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b2ca0a7..b11c4c9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -29,11 +29,13 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceSession.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <private/gui/ComposerService.h>
#include <stdio.h>
#include <system/graphics.h>
#include <ui/ConfigStoreTypes.h>
@@ -624,6 +626,23 @@
transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility));
}
+static jlong nativeAcquireFrameRateFlexibilityToken(JNIEnv* env, jclass clazz) {
+ sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+ sp<IBinder> token;
+ status_t result = composer->acquireFrameRateFlexibilityToken(&token);
+ if (result < 0) {
+ ALOGE("Failed acquiring frame rate flexibility token: %s (%d)", strerror(-result), result);
+ return 0;
+ }
+ token->incStrong((void*)nativeAcquireFrameRateFlexibilityToken);
+ return reinterpret_cast<jlong>(token.get());
+}
+
+static void nativeReleaseFrameRateFlexibilityToken(JNIEnv* env, jclass clazz, jlong tokenLong) {
+ sp<IBinder> token(reinterpret_cast<IBinder*>(tokenLong));
+ token->decStrong((void*)nativeAcquireFrameRateFlexibilityToken);
+}
+
static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
jlongArray array = env->NewLongArray(displayIds.size());
@@ -1474,6 +1493,10 @@
(void*)nativeSetShadowRadius },
{"nativeSetFrameRate", "(JJFI)V",
(void*)nativeSetFrameRate },
+ {"nativeAcquireFrameRateFlexibilityToken", "()J",
+ (void*)nativeAcquireFrameRateFlexibilityToken },
+ {"nativeReleaseFrameRateFlexibilityToken", "(J)V",
+ (void*)nativeReleaseFrameRateFlexibilityToken },
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 41aa1ff..5088494 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -418,9 +418,9 @@
auto data = picture->serialize();
savePictureAsync(data, mCapturedFile);
mCaptureSequence = 0;
+ mCaptureMode = CaptureMode::None;
}
}
- mCaptureMode = CaptureMode::None;
mRecorder.reset();
}
}
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 90bcd1c..1208062 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -403,3 +403,40 @@
renderThread.destroyRenderingContext();
EXPECT_FALSE(pipeline->isSurfaceReady());
}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+ // create a pipeline and add a picture callback
+ auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+ int callbackCount = 0;
+ pipeline->setPictureCapturedCallback(
+ [&callbackCount](sk_sp<SkPicture>&& picture) { callbackCount += 1; });
+
+ // create basic red frame and render it
+ auto redNode = TestUtils::createSkiaNode(
+ 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+ redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+ });
+ LayerUpdateQueue layerUpdateQueue;
+ SkRect dirty = SkRectMakeLargest();
+ std::vector<sp<RenderNode>> renderNodes;
+ renderNodes.push_back(redNode);
+ bool opaque = true;
+ android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+ auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+ SkMatrix::I());
+
+ // verify the callback was called
+ EXPECT_EQ(1, callbackCount);
+
+ // render a second frame and check the callback count
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+ SkMatrix::I());
+ EXPECT_EQ(2, callbackCount);
+
+ // unset the callback, render another frame, check callback was not invoked
+ pipeline->setPictureCapturedCallback(nullptr);
+ pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+ SkMatrix::I());
+ EXPECT_EQ(2, callbackCount);
+}
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 88a8295..540955f 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -17,6 +17,7 @@
package android.media;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Bundle;
@@ -24,6 +25,7 @@
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.Objects;
/**
* MediaMetrics is the Java interface to the MediaMetrics service.
@@ -50,6 +52,77 @@
private static final Charset MEDIAMETRICS_CHARSET = StandardCharsets.UTF_8;
/**
+ * Key interface.
+ *
+ * The presence of this {@code Key} interface on an object allows
+ * it to be used to set metrics.
+ *
+ * @param <T> type of value associated with {@code Key}.
+ */
+ public interface Key<T> {
+ /**
+ * Returns the internal name of the key.
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * Returns the class type of the associated value.
+ */
+ @NonNull
+ Class<T> getValueClass();
+ }
+
+ /**
+ * Returns a Key object with the correct interface for MediaMetrics.
+ *
+ * @param name The name of the key.
+ * @param type The class type of the value represented by the key.
+ * @param <T> The type of value.
+ * @return a new key interface.
+ */
+ @NonNull
+ public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) {
+ // Implementation specific.
+ return new Key<T>() {
+ private final String mName = name;
+ private final Class<T> mType = type;
+
+ @Override
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ @NonNull
+ public Class<T> getValueClass() {
+ return mType;
+ }
+
+ /**
+ * Return true if the name and the type of two objects are the same.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Key)) {
+ return false;
+ }
+ Key<?> other = (Key<?>) obj;
+ return mName.equals(other.getName()) && mType.equals(other.getValueClass());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mType);
+ }
+ };
+ }
+
+ /**
* Item records properties and delivers to the MediaMetrics service
*
*/
@@ -202,6 +275,28 @@
}
/**
+ * Sets a metrics typed key
+ * @param key
+ * @param value
+ * @param <T>
+ * @return
+ */
+ @NonNull
+ public <T> Item set(@NonNull Key<T> key, @Nullable T value) {
+ if (value instanceof Integer) {
+ putInt(key.getName(), (int) value);
+ } else if (value instanceof Long) {
+ putLong(key.getName(), (long) value);
+ } else if (value instanceof Double) {
+ putDouble(key.getName(), (double) value);
+ } else if (value instanceof String) {
+ putString(key.getName(), (String) value);
+ }
+ // if value is null, etc. no error is raised.
+ return this;
+ }
+
+ /**
* Sets the property with key to an integer (32 bit) value.
*
* @param key
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 66f45ca..53e7921 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -604,7 +604,7 @@
<!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing scanner title -->
<string name="adb_wireless_qrcode_pairing_title">Scan QR code</string>
<!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing description -->
- <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR Code</string>
+ <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR code</string>
<!-- [CHAR LIMIT=NONE] Toast message when trying to enable Wi-Fi debugging and no Wi-Fi network connected -->
<string name="adb_wireless_no_network_msg">Please connect to a Wi\u2011Fi network</string>
<!--Adb wireless search Keywords [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 1696f07..53ec570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -103,12 +103,20 @@
override fun onEntryInflated(entry: NotificationEntry) {
if (!entry.ranking.isConversation) return
fun updateCount(isExpanded: Boolean) {
- if (isExpanded && !notifPanelCollapsed) {
+ if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) {
resetCount(entry.key)
entry.row?.let(::resetBadgeUi)
}
}
- entry.row?.setOnExpansionChangedListener(::updateCount)
+ entry.row?.setOnExpansionChangedListener { isExpanded ->
+ if (entry.row?.isShown == true && isExpanded) {
+ entry.row.performOnIntrinsicHeightReached {
+ updateCount(isExpanded)
+ }
+ } else {
+ updateCount(isExpanded)
+ }
+ }
updateCount(entry.row?.isExpanded == true)
}
@@ -169,7 +177,8 @@
private fun resetBadgeUi(row: ExpandableNotificationRow): Unit =
(row.layouts?.asSequence() ?: emptySequence())
- .mapNotNull { layout -> layout.contractedChild as? ConversationLayout }
+ .flatMap { layout -> layout.allViews.asSequence()}
+ .mapNotNull { view -> view as? ConversationLayout }
.forEach { convoLayout -> convoLayout.setUnreadCount(0) }
private data class ConversationState(val unreadCount: Int, val notification: Notification)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index dd7be27..c1ba26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -601,6 +601,13 @@
return row != null && row.isPinned();
}
+ /**
+ * Is this entry pinned and was expanded while doing so
+ */
+ public boolean isPinnedAndExpanded() {
+ return row != null && row.isPinnedAndExpanded();
+ }
+
public void setRowPinned(boolean pinned) {
if (row != null) row.setPinned(pinned);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 2917346..5c578df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -284,6 +284,12 @@
if (isPinned()) {
nowExpanded = !mExpandedWhenPinned;
mExpandedWhenPinned = nowExpanded;
+ // Also notify any expansion changed listeners. This is necessary since the
+ // expansion doesn't actually change (it's already system expanded) but it
+ // changes visually
+ if (mExpansionChangedListener != null) {
+ mExpansionChangedListener.onExpansionChanged(nowExpanded);
+ }
} else {
nowExpanded = !isExpanded();
setUserExpanded(nowExpanded);
@@ -326,6 +332,7 @@
private NotificationInlineImageResolver mImageResolver;
private NotificationMediaManager mMediaManager;
@Nullable private OnExpansionChangedListener mExpansionChangedListener;
+ @Nullable private Runnable mOnIntrinsicHeightReachedRunnable;
private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
new SystemNotificationAsyncTask();
@@ -358,6 +365,16 @@
return Arrays.copyOf(mLayouts, mLayouts.length);
}
+ /**
+ * Is this entry pinned and was expanded while doing so
+ */
+ public boolean isPinnedAndExpanded() {
+ if (!isPinned()) {
+ return false;
+ }
+ return mExpandedWhenPinned;
+ }
+
@Override
public boolean isGroupExpansionChanging() {
if (isChildInGroup()) {
@@ -2690,6 +2707,7 @@
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onParentHeightUpdate();
}
+ handleIntrinsicHeightReached();
}
@Override
@@ -2907,6 +2925,24 @@
mExpansionChangedListener = listener;
}
+ /**
+ * Perform an action when the notification height has reached its intrinsic height.
+ *
+ * @param runnable the runnable to run
+ */
+ public void performOnIntrinsicHeightReached(@Nullable Runnable runnable) {
+ mOnIntrinsicHeightReachedRunnable = runnable;
+ handleIntrinsicHeightReached();
+ }
+
+ private void handleIntrinsicHeightReached() {
+ if (mOnIntrinsicHeightReachedRunnable != null
+ && getActualHeight() == getIntrinsicHeight()) {
+ mOnIntrinsicHeightReachedRunnable.run();
+ mOnIntrinsicHeightReachedRunnable = null;
+ }
+ }
+
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b18bf01..3c3f1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
@@ -988,6 +989,14 @@
}
}
+ public @NonNull View[] getAllViews() {
+ return new View[] {
+ mContractedChild,
+ mHeadsUpChild,
+ mExpandedChild,
+ mSingleLineView };
+ }
+
public NotificationViewWrapper getVisibleWrapper(int visibleType) {
switch (visibleType) {
case VISIBLE_TYPE_EXPANDED:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f06cfec..82e02b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -203,7 +203,9 @@
Log.wtf(TAG, "onFullyShown when view was null");
} else {
mKeyguardView.onResume();
- mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
+ if (mRoot != null) {
+ mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index d70484e..a19d35a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -50,19 +50,22 @@
static final int STATE_BIOMETRICS_ERROR = 3;
private float mDozeAmount;
private int mIconColor;
- private StateProvider mStateProvider;
private int mOldState;
+ private int mState;
private boolean mPulsing;
private boolean mDozing;
private boolean mKeyguardJustShown;
+ private boolean mPredrawRegistered;
private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
+ mPredrawRegistered = false;
- int newState = mStateProvider.getState();
+ int newState = mState;
+ mOldState = mState;
Drawable icon = getIcon(newState);
setImageDrawable(icon, false);
@@ -80,7 +83,7 @@
@Override
public void onAnimationEnd(Drawable drawable) {
if (getDrawable() == animation
- && newState == mStateProvider.getState()
+ && newState == mState
&& newState == STATE_SCANNING_FACE) {
animation.start();
} else {
@@ -100,10 +103,6 @@
super(context, attrs);
}
- void setStateProvider(StateProvider stateProvider) {
- mStateProvider = stateProvider;
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -135,13 +134,16 @@
return false;
}
- void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
- mOldState = oldState;
+ void update(int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+ mState = newState;
mPulsing = pulsing;
mDozing = dozing;
mKeyguardJustShown = keyguardJustShown;
- getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+ if (!mPredrawRegistered) {
+ mPredrawRegistered = true;
+ getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+ }
}
void setDozeAmount(float dozeAmount) {
@@ -175,7 +177,7 @@
return mDrawableCache.get(iconRes);
}
- static int getIconForState(int state) {
+ private static int getIconForState(int state) {
int iconRes;
switch (state) {
case STATE_LOCKED:
@@ -196,7 +198,7 @@
return iconRes;
}
- static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+ private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
boolean dozing, boolean keyguardJustShown) {
// Never animate when screen is off
@@ -260,9 +262,4 @@
}
return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
}
-
- interface StateProvider {
- int getState();
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index f7c861b..a633e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -352,7 +352,6 @@
mLockIcon.setOnClickListener(this::handleClick);
mLockIcon.setOnLongClickListener(this::handleLongClick);
mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
- mLockIcon.setStateProvider(this::getState);
if (mLockIcon.isAttachedToWindow()) {
mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
@@ -462,7 +461,7 @@
shouldUpdate = false;
}
if (shouldUpdate && mLockIcon != null) {
- mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+ mLockIcon.update(state, mPulsing, mDozing, mKeyguardJustShown);
}
mLastState = state;
mKeyguardJustShown = false;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index bae5bb4..c22b718f 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -272,13 +272,18 @@
}
mAnimation.cancel();
}
- mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
final float defaultY = mImeSourceControl.getSurfacePosition().y;
final float x = mImeSourceControl.getSurfacePosition().x;
final float hiddenY = defaultY + imeSource.getFrame().height();
final float shownY = defaultY;
final float startY = show ? hiddenY : shownY;
final float endY = show ? shownY : hiddenY;
+ if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) {
+ // IME is already showing, so set seek to end
+ seekValue = shownY;
+ seek = true;
+ }
+ mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
mImeShowing = show;
mAnimation = ValueAnimator.ofFloat(startY, endY);
mAnimation.setDuration(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bc3a035..f693555 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9684,6 +9684,20 @@
@Override
public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
String loaderIsa) {
+ if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName)
+ && Binder.getCallingUid() != Process.SYSTEM_UID) {
+ Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
+ + Binder.getCallingUid());
+ // Do not record dex loads from processes pretending to be system server.
+ // Only the system server should be assigned the package "android", so reject calls
+ // that don't satisfy the constraint.
+ //
+ // notifyDexLoad is a PM API callable from the app process. So in theory, apps could
+ // craft calls to this API and pretend to be system server. Doing so poses no particular
+ // danger for dex load reporting or later dexopt, however it is a sensible check to do
+ // in order to verify the expectations.
+ return;
+ }
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -11310,7 +11324,7 @@
Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
parsedPackage.getPackageName() + " " +
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
+ + ", "
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
}
}
@@ -17572,11 +17586,13 @@
}
if (needToVerify) {
+ final boolean needsVerification = needsNetworkVerificationLPr(packageName);
final int verificationId = mIntentFilterVerificationToken++;
for (ParsedActivity a : activities) {
for (ParsedIntentInfo filter : a.getIntents()) {
- if (filter.handlesWebUris(true)
- && needsNetworkVerificationLPr(a.getPackageName())) {
+ // Run verification against hosts mentioned in any web-nav intent filter,
+ // even if the filter matches non-web schemes as well
+ if (needsVerification && filter.handlesWebUris(false)) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Verification needed for IntentFilter:" + filter.toString());
mIntentFilterVerifier.addOneIntentFilterVerification(
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index e8765ad..ebdf856 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -17,13 +17,17 @@
package com.android.server.pm.dex;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static java.util.function.Function.identity;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackagePartitions;
import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -67,13 +71,12 @@
*/
public class DexManager {
private static final String TAG = "DexManager";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
"pm.dexopt.priv-apps-oob-list";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
private final Context mContext;
// Maps package name to code locations.
@@ -178,12 +181,14 @@
boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY ||
searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT;
- if (primaryOrSplit && !isUsedByOtherApps) {
+ if (primaryOrSplit && !isUsedByOtherApps
+ && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) {
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
// do not record it. This case does not bring any new usable information
// and can be safely skipped.
// Note this is just an optimization that makes things easier to read in the
// package-dex-use file since we don't need to pollute it with redundant info.
+ // However, we always record system server packages.
continue;
}
@@ -217,6 +222,23 @@
}
/**
+ * Check if the dexPath belongs to system server.
+ * System server can load code from different location, so we cast a wide-net here, and
+ * assume that if the paths is on any of the registered system partitions then it can be loaded
+ * by system server.
+ */
+ private boolean isSystemServerDexPathSupportedForOdex(String dexPath) {
+ ArrayList<PackagePartitions.SystemPartition> partitions =
+ PackagePartitions.getOrderedPartitions(identity());
+ for (int i = 0; i < partitions.size(); i++) {
+ if (partitions.get(i).containsPath(dexPath)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Read the dex usage from disk and populate the code cache locations.
* @param existingPackages a map containing information about what packages
* are available to what users. Only packages in this list will be
@@ -607,12 +629,6 @@
*/
private DexSearchResult getDexPackage(
ApplicationInfo loadingAppInfo, String dexPath, int userId) {
- // Ignore framework code.
- // TODO(calin): is there a better way to detect it?
- if (dexPath.startsWith("/system/framework/")) {
- return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
- }
-
// First, check if the package which loads the dex file actually owns it.
// Most of the time this will be true and we can return early.
PackageCodeLocations loadingPackageCodeLocations =
@@ -635,6 +651,28 @@
}
}
+ // We could not find the owning package amongst regular apps.
+ // If the loading package is system server, see if the dex file resides
+ // on any of the potentially system server owning location and if so,
+ // assuming system server ownership.
+ //
+ // Note: We don't have any way to detect which code paths are actually
+ // owned by system server. We can only assume that such paths are on
+ // system partitions.
+ if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) {
+ if (isSystemServerDexPathSupportedForOdex(dexPath)) {
+ // We record system server dex files as secondary dex files.
+ // The reason is that we only record the class loader context for secondary dex
+ // files and we expect that all primary apks are loaded with an empty class loader.
+ // System server dex files may be loaded in non-empty class loader so we need to
+ // keep track of their context.
+ return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY);
+ } else {
+ Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: "
+ + dexPath);
+ }
+ }
+
if (DEBUG) {
// TODO(calin): Consider checking for /data/data symlink.
// /data/data/ symlinks /data/user/0/ and there's nothing stopping apps
diff --git a/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java
new file mode 100644
index 0000000..807c82d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.pm.dex;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import dalvik.system.BaseDexClassLoader;
+import dalvik.system.VMRuntime;
+
+import java.util.Map;
+
+/**
+ * Reports dex file use to the package manager on behalf of system server.
+ */
+public class SystemServerDexLoadReporter implements BaseDexClassLoader.Reporter {
+ private static final String TAG = "SystemServerDexLoadReporter";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final IPackageManager mPackageManager;
+
+ private SystemServerDexLoadReporter(IPackageManager pm) {
+ mPackageManager = pm;
+ }
+
+ @Override
+ public void report(Map<String, String> classLoaderContextMap) {
+ if (DEBUG) {
+ Slog.i(TAG, "Reporting " + classLoaderContextMap);
+ }
+ if (classLoaderContextMap.isEmpty()) {
+ Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
+ return;
+ }
+
+ try {
+ mPackageManager.notifyDexLoad(
+ PLATFORM_PACKAGE_NAME,
+ classLoaderContextMap,
+ VMRuntime.getRuntime().vmInstructionSet());
+ } catch (RemoteException ignored) {
+ // We're in system server, it can't happen.
+ }
+ }
+
+ /**
+ * Configures system server dex file reporting.
+ * <p>The method will install a reporter in the BaseDexClassLoader and also
+ * force the reporting of any dex files already loaded by the system server.
+ */
+ public static void configureSystemServerDexReporter(IPackageManager pm) {
+ Slog.i(TAG, "Configuring system server dex reporter");
+
+ SystemServerDexLoadReporter reporter = new SystemServerDexLoadReporter(pm);
+ BaseDexClassLoader.setReporter(reporter);
+ ClassLoader currrentClassLoader = reporter.getClass().getClassLoader();
+ if (currrentClassLoader instanceof BaseDexClassLoader) {
+ ((BaseDexClassLoader) currrentClassLoader).reportClassLoaderChain();
+ } else {
+ Slog.wtf(TAG, "System server class loader is not a BaseDexClassLoader. type="
+ + currrentClassLoader.getClass().getName());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 90fdf19..edd14b7 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -253,6 +253,12 @@
super.prepareSurfaces();
getBounds(mTmpDimBoundsRect);
+ // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
+ // on the display level fades out.
+ if (forAllTasks(task -> !task.canAffectSystemUiFlags())) {
+ mDimmer.resetDimStates();
+ }
+
if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
scheduleAnimation();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f088dbc..e9d3d56 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -696,6 +696,10 @@
mForceShowSystemBarsFromExternal = forceShowSystemBars;
}
+ boolean getForceShowSystemBars() {
+ return mForceShowSystemBarsFromExternal;
+ }
+
public boolean hasNavigationBar() {
return mHasNavigationBar;
}
@@ -1494,9 +1498,7 @@
final int behavior = mLastBehavior;
boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
- : mNavigationBar != null && mNavigationBar.getControllableInsetProvider() != null
- && mNavigationBar.getControllableInsetProvider().isClientVisible()
- && !mDisplayContent.getInsetsPolicy().isTransient(ITYPE_NAVIGATION_BAR);
+ : isNavigationBarRequestedVisible();
boolean navTranslucent = (sysui
& (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0
@@ -1533,6 +1535,14 @@
mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
+ boolean isNavigationBarRequestedVisible() {
+ final InsetsSourceProvider provider =
+ mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR);
+ return provider == null
+ ? InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR)
+ : provider.isClientVisible();
+ }
+
void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
// When the navigation bar isn't visible, we put up a fake input window to catch all
// touch events. This way we can detect when the user presses anywhere to bring back the
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index e4e5716..61a199a 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -99,7 +99,7 @@
}
private void updateHideNavInputEventReceiver() {
- mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+ mPolicy.updateHideNavInputEventReceiver(mPolicy.isNavigationBarRequestedVisible(),
mFocusedWin != null
&& mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
}
@@ -304,7 +304,10 @@
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
- return isDockedStackVisible || isFreeformStackVisible || isResizing;
+ return isDockedStackVisible
+ || isFreeformStackVisible
+ || isResizing
+ || mPolicy.getForceShowSystemBars();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cb897db..9adacb8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3348,6 +3348,21 @@
@Override
Dimmer getDimmer() {
+ // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim
+ // bounds match the area the app lives in
+ if (inMultiWindowMode()) {
+ return mDimmer;
+ }
+
+ // If we're not at the root task level, we want to keep traversing through the parents to
+ // find the root.
+ // Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}.
+ // If true, we want to get the Dimmer from the level above since we don't want to animate
+ // the dim with the Task.
+ if (!isRootTask() || isTranslucent(null)) {
+ return super.getDimmer();
+ }
+
return mDimmer;
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index d36eae8..de24bcf 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -73,7 +73,7 @@
};
static const Constants& constants() {
- static Constants c;
+ static constexpr Constants c;
return c;
}
@@ -159,6 +159,9 @@
}
} // namespace
+const bool IncrementalService::sEnablePerfLogging =
+ android::base::GetBoolProperty("incremental.perflogging", false);
+
IncrementalService::IncFsMount::~IncFsMount() {
incrementalService.mDataLoaderManager->destroyDataLoader(mountId);
LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
@@ -719,7 +722,10 @@
if (storageInfo == ifs->storages.end()) {
return -EINVAL;
}
- std::string normSource = normalizePathToStorage(ifs, storage, source);
+ std::string normSource = normalizePathToStorageLocked(storageInfo, source);
+ if (normSource.empty()) {
+ return -EINVAL;
+ }
l.unlock();
std::unique_lock l2(mLock, std::defer_lock);
return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -768,22 +774,28 @@
return 0;
}
-std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs,
+std::string IncrementalService::normalizePathToStorageLocked(
+ IncFsMount::StorageMap::iterator storageIt, std::string_view path) {
+ std::string normPath;
+ if (path::isAbsolute(path)) {
+ normPath = path::normalize(path);
+ if (!path::startsWith(normPath, storageIt->second.name)) {
+ return {};
+ }
+ } else {
+ normPath = path::normalize(path::join(storageIt->second.name, path));
+ }
+ return normPath;
+}
+
+std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr& ifs,
StorageId storage, std::string_view path) {
+ std::unique_lock l(ifs->lock);
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
return {};
}
- std::string normPath;
- if (path::isAbsolute(path)) {
- normPath = path::normalize(path);
- } else {
- normPath = path::normalize(path::join(storageInfo->second.name, path));
- }
- if (!path::startsWith(normPath, storageInfo->second.name)) {
- return {};
- }
- return normPath;
+ return normalizePathToStorageLocked(storageInfo, path);
}
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -791,7 +803,8 @@
if (auto ifs = getIfs(storage)) {
std::string normPath = normalizePathToStorage(ifs, storage, path);
if (normPath.empty()) {
- LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
+ LOG(ERROR) << "Internal error: storageId " << storage
+ << " failed to normalize: " << path;
return -EINVAL;
}
auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -799,10 +812,6 @@
LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
return err;
}
- std::vector<uint8_t> metadataBytes;
- if (params.metadata.data && params.metadata.size > 0) {
- metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
- }
return 0;
}
return -EINVAL;
@@ -842,8 +851,9 @@
int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
StorageId destStorageId, std::string_view newPath) {
- if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
- ifsSrc && ifsSrc == ifsDest) {
+ auto ifsSrc = getIfs(sourceStorageId);
+ auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId);
+ if (ifsSrc && ifsSrc == ifsDest) {
std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
if (normOldPath.empty() || normNewPath.empty()) {
@@ -1156,11 +1166,25 @@
return true;
}
-// Extract lib filse from zip, create new files in incfs and write data to them
+template <class Duration>
+static long elapsedMcs(Duration start, Duration end) {
+ return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
+}
+
+// Extract lib files from zip, create new files in incfs and write data to them
bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
std::string_view libDirRelativePath,
std::string_view abi) {
+ namespace sc = std::chrono;
+ using Clock = sc::steady_clock;
+ auto start = Clock::now();
+
const auto ifs = getIfs(storage);
+ if (!ifs) {
+ LOG(ERROR) << "Invalid storage " << storage;
+ return false;
+ }
+
// First prepare target directories if they don't exist yet
if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
@@ -1168,112 +1192,145 @@
return false;
}
- std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data()));
+ auto mkDirsTs = Clock::now();
+
+ std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(path::c_str(apkFullPath)));
if (!zipFile) {
LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
return false;
}
void* cookie = nullptr;
const auto libFilePrefix = path::join(constants().libDir, abi);
- if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
- constants().libSuffix.data() /* suffix */)) {
+ if (!zipFile->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
+ constants().libSuffix.data() /* suffix */)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false;
}
+ auto endIteration = [&zipFile](void* cookie) { zipFile->endIteration(cookie); };
+ auto iterationCleaner = std::unique_ptr<void, decltype(endIteration)>(cookie, endIteration);
+
+ auto openZipTs = Clock::now();
+
+ std::vector<IncFsDataBlock> instructions;
ZipEntryRO entry = nullptr;
- bool success = true;
- while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) {
+ while ((entry = zipFile->nextEntry(cookie)) != nullptr) {
+ auto startFileTs = Clock::now();
+
char fileName[PATH_MAX];
- if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) {
+ if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
continue;
}
const auto libName = path::basename(fileName);
const auto targetLibPath = path::join(libDirRelativePath, libName);
const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
// If the extract file already exists, skip
- struct stat st;
- if (stat(targetLibPathAbsolute.c_str(), &st) == 0) {
- LOG(INFO) << "Native lib file already exists: " << targetLibPath
- << "; skipping extraction";
+ if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
+ if (sEnablePerfLogging) {
+ LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath
+ << "; skipping extraction, spent "
+ << elapsedMcs(startFileTs, Clock::now()) << "mcs";
+ }
continue;
}
- uint32_t uncompressedLen;
- if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr,
- nullptr, nullptr)) {
+ uint32_t uncompressedLen, compressedLen;
+ if (!zipFile->getEntryInfo(entry, nullptr, &uncompressedLen, &compressedLen, nullptr,
+ nullptr, nullptr)) {
LOG(ERROR) << "Failed to read native lib entry: " << fileName;
- success = false;
- break;
+ return false;
}
// Create new lib file without signature info
- incfs::NewFileParams libFileParams{};
- libFileParams.size = uncompressedLen;
- libFileParams.signature = {};
- // Metadata of the new lib file is its relative path
- IncFsSpan libFileMetadata;
- libFileMetadata.data = targetLibPath.c_str();
- libFileMetadata.size = targetLibPath.size();
- libFileParams.metadata = libFileMetadata;
+ incfs::NewFileParams libFileParams = {
+ .size = uncompressedLen,
+ .signature = {},
+ // Metadata of the new lib file is its relative path
+ .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
+ };
incfs::FileId libFileId = idFromMetadata(targetLibPath);
- if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) {
+ if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId,
+ libFileParams)) {
LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
- success = false;
// If one lib file fails to be created, abort others as well
- break;
+ return false;
}
+
+ auto makeFileTs = Clock::now();
+
// If it is a zero-byte file, skip data writing
if (uncompressedLen == 0) {
+ if (sEnablePerfLogging) {
+ LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
+ << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, makeFileTs)
+ << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs);
+ }
continue;
}
// Write extracted data to new file
- std::vector<uint8_t> libData(uncompressedLen);
- if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) {
+ // NOTE: don't zero-initialize memory, it may take a while
+ auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[uncompressedLen]);
+ if (!zipFile->uncompressEntry(entry, libData.get(), uncompressedLen)) {
LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName;
- success = false;
- break;
+ return false;
}
+
+ auto extractFileTs = Clock::now();
+
const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
if (!writeFd.ok()) {
LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
- success = false;
- break;
+ return false;
}
- const int numBlocks = uncompressedLen / constants().blockSize + 1;
- std::vector<IncFsDataBlock> instructions;
- auto remainingData = std::span(libData);
- for (int i = 0; i < numBlocks - 1; i++) {
+
+ auto openFileTs = Clock::now();
+
+ const int numBlocks = (uncompressedLen + constants().blockSize - 1) / constants().blockSize;
+ instructions.clear();
+ instructions.reserve(numBlocks);
+ auto remainingData = std::span(libData.get(), uncompressedLen);
+ for (int i = 0; i < numBlocks; i++) {
+ const auto blockSize = std::min<uint16_t>(constants().blockSize, remainingData.size());
auto inst = IncFsDataBlock{
- .fileFd = writeFd,
+ .fileFd = writeFd.get(),
.pageIndex = static_cast<IncFsBlockIndex>(i),
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = static_cast<uint16_t>(constants().blockSize),
+ .dataSize = blockSize,
.data = reinterpret_cast<const char*>(remainingData.data()),
};
instructions.push_back(inst);
- remainingData = remainingData.subspan(constants().blockSize);
+ remainingData = remainingData.subspan(blockSize);
}
- // Last block
- auto inst = IncFsDataBlock{
- .fileFd = writeFd,
- .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1),
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = static_cast<uint16_t>(remainingData.size()),
- .data = reinterpret_cast<const char*>(remainingData.data()),
- };
- instructions.push_back(inst);
+ auto prepareInstsTs = Clock::now();
+
size_t res = mIncFs->writeBlocks(instructions);
if (res != instructions.size()) {
LOG(ERROR) << "Failed to write data into: " << targetLibPath;
- success = false;
+ return false;
}
- instructions.clear();
+
+ if (sEnablePerfLogging) {
+ auto endFileTs = Clock::now();
+ LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
+ << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, endFileTs)
+ << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs)
+ << " extract: " << elapsedMcs(makeFileTs, extractFileTs)
+ << " open: " << elapsedMcs(extractFileTs, openFileTs)
+ << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
+ << " write:" << elapsedMcs(prepareInstsTs, endFileTs);
+ }
}
- zipFile.get()->endIteration(cookie);
- return success;
+
+ if (sEnablePerfLogging) {
+ auto end = Clock::now();
+ LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end)
+ << "mcs, make dirs: " << elapsedMcs(start, mkDirsTs)
+ << " open zip: " << elapsedMcs(mkDirsTs, openZipTs)
+ << " extract all: " << elapsedMcs(openZipTs, end);
+ }
+
+ return true;
}
void IncrementalService::registerAppOpsCallback(const std::string& packageName) {
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 5800297..9b15646 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -157,6 +157,8 @@
};
private:
+ static const bool sEnablePerfLogging;
+
struct IncFsMount {
struct Bind {
StorageId storage;
@@ -227,8 +229,10 @@
void deleteStorage(IncFsMount& ifs);
void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
MountMap::iterator getStorageSlotLocked();
- std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
+ std::string normalizePathToStorage(const IfsMountPtr& incfs, StorageId storage,
std::string_view path);
+ std::string normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt,
+ std::string_view path);
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e5ffbac..e2a2473 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.dex.SystemServerDexLoadReporter;
import com.android.server.policy.PermissionPolicyService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.policy.role.LegacyRoleResolutionPolicy;
@@ -837,6 +838,11 @@
Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
}
+ // Now that the package manager has started, register the dex load reporter to capture any
+ // dex files loaded by system server.
+ // These dex files will be optimized by the BackgroundDexOptService.
+ SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);
+
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
t.traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index d69e1b8..8398585 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -85,6 +85,9 @@
private TestData mBarUser0UnsupportedClassLoader;
private TestData mBarUser0DelegateLastClassLoader;
+ private TestData mSystemServerJar;
+ private TestData mSystemServerJarInvalid;
+
private int mUser0;
private int mUser1;
@@ -108,6 +111,9 @@
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
DELEGATE_LAST_CLASS_LOADER_NAME);
+ mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
+ mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
+
mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
mInstaller, mInstallLock);
@@ -587,6 +593,25 @@
assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
}
+
+ @Test
+ public void testNotifySystemServerUse() {
+ List<String> dexFiles = new ArrayList<String>();
+ dexFiles.add("/system/framework/foo");
+ notifyDexLoad(mSystemServerJar, dexFiles, mUser0);
+ PackageUseInfo pui = getPackageUseInfo(mSystemServerJar);
+ assertIsUsedByOtherApps(mSystemServerJar, pui, false);
+ }
+
+ @Test
+ public void testNotifySystemServerInvalidUse() {
+ List<String> dexFiles = new ArrayList<String>();
+ dexFiles.add("/data/foo");
+ notifyDexLoad(mSystemServerJarInvalid, dexFiles, mUser0);
+ assertNoUseInfo(mSystemServerJarInvalid);
+ assertNoDclInfo(mSystemServerJarInvalid);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 89bc65b..b6eb901 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -30,7 +30,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -41,7 +40,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.util.IntArray;
@@ -49,7 +47,6 @@
import android.view.InsetsState;
import android.view.test.InsetsModeSession;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.AfterClass;
@@ -168,6 +165,18 @@
}
@Test
+ public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
+ mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
+ addWindow(TYPE_STATUS_BAR, "statusBar");
+ addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+ final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+ // The focused app window cannot control system bars.
+ assertNull(controls);
+ }
+
+ @Test
public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
.getControllableInsetProvider().getSource().setVisible(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1ca2e318..397f73c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -134,6 +134,7 @@
mChildAppWindowBelow = createCommonWindow(mAppWindow,
TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
+ mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
}
// Adding a display will cause freezing the display. Make sure to wait until it's