diff options
46 files changed, 1006 insertions, 81 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 80664ed0816f..a4ac61b1f086 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1769,6 +1769,7 @@ package android.os { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 9a8194240f8e..3bee4b73dc22 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -46,6 +46,7 @@ interface IUserManager { UserInfo createProfileForUserWithThrow(in String name, in String userType, int flags, int userId, in String[] disallowedPackages); UserInfo createRestrictedProfileWithThrow(String name, int parentUserHandle); + String[] getPreInstallableSystemPackages(in String userType); void setUserEnabled(int userId); void setUserAdmin(int userId); void evictCredentialEncryptionKey(int userId); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7edd6e6597b2..8709f071f222 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressAutoDoc; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -53,6 +54,7 @@ import android.location.LocationManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.AndroidException; +import android.util.ArraySet; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -3221,6 +3223,33 @@ public class UserManager { } /** + * Returns the list of the system packages that would be installed on this type of user upon + * its creation. + * + * Returns {@code null} if all system packages would be installed. + * + * @hide + */ + @TestApi + @SuppressLint("NullableCollection") + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public @Nullable Set<String> getPreInstallableSystemPackages(@NonNull String userType) { + try { + final String[] installableSystemPackages + = mService.getPreInstallableSystemPackages(userType); + if (installableSystemPackages == null) { + return null; + } + return new ArraySet<>(installableSystemPackages); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * @hide * * Returns the preferred account name for user creation. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 36bc3e779c12..cb87653718c2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6661,6 +6661,20 @@ public final class Settings { public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category."; /** + * Whether or not compress blocks should be released on install. + * <p>The setting only determines if the platform will attempt to release + * compress blocks; it does not guarantee that the files will have their + * compress blocks released. Compression is currently only supported on + * some f2fs filesystems. + * <p> + * Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String RELEASE_COMPRESS_BLOCKS_ON_INSTALL = + "release_compress_blocks_on_install"; + + /** * List of input methods that are currently enabled. This is a string * containing the IDs of all enabled input methods, each ID separated * by ':'. diff --git a/core/java/com/android/internal/content/F2fsUtils.java b/core/java/com/android/internal/content/F2fsUtils.java new file mode 100644 index 000000000000..27f1b308ed9c --- /dev/null +++ b/core/java/com/android/internal/content/F2fsUtils.java @@ -0,0 +1,296 @@ +/* + * 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.internal.content; + +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.os.Environment; +import android.os.incremental.IncrementalManager; +import android.provider.Settings.Secure; +import android.text.TextUtils; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility methods to work with the f2fs file system. + */ +public final class F2fsUtils { + private static final String TAG = "F2fsUtils"; + private static final boolean DEBUG_F2FS = false; + + /** Directory containing kernel features */ + private static final File sKernelFeatures = + new File("/sys/fs/f2fs/features"); + /** File containing features enabled on "/data" */ + private static final File sUserDataFeatures = + new File("/dev/sys/fs/by-name/userdata/features"); + private static final File sDataDirectory = Environment.getDataDirectory(); + /** Name of the compression feature */ + private static final String COMPRESSION_FEATURE = "compression"; + + private static final boolean sKernelCompressionAvailable; + private static final boolean sUserDataCompressionAvailable; + + static { + sKernelCompressionAvailable = isCompressionEnabledInKernel(); + if (!sKernelCompressionAvailable) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; feature not part of the kernel"); + } + } + sUserDataCompressionAvailable = isCompressionEnabledOnUserData(); + if (!sUserDataCompressionAvailable) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; feature not enabled on filesystem"); + } + } + } + + /** + * Releases compressed blocks from eligible installation artifacts. + * <p> + * Modern f2fs implementations starting in {@code S} support compression + * natively within the file system. The data blocks of specific installation + * artifacts [eg. .apk, .so, ...] can be compressed at the file system level, + * making them look and act like any other uncompressed file, but consuming + * a fraction of the space. + * <p> + * However, the unused space is not free'd automatically. Instead, we must + * manually tell the file system to release the extra blocks [the delta between + * the compressed and uncompressed block counts] back to the free pool. + * <p> + * Because of how compression works within the file system, once the blocks + * have been released, the file becomes read-only and cannot be modified until + * the free'd blocks have again been reserved from the free pool. + */ + public static void releaseCompressedBlocks(ContentResolver resolver, File file) { + if (!sKernelCompressionAvailable || !sUserDataCompressionAvailable) { + return; + } + + // NOTE: Retrieving this setting means we need to delay releasing cblocks + // of any APKs installed during the PackageManagerService constructor. Instead + // of being able to release them in the constructor, they can only be released + // immediately prior to the system being available. When we no longer need to + // read this setting, move cblock release back to the package manager constructor. + final boolean releaseCompressBlocks = + Secure.getInt(resolver, Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, 1) != 0; + if (!releaseCompressBlocks) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; release compress blocks not enabled"); + } + return; + } + if (!isCompressionAllowed(file)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; compression not allowed"); + } + return; + } + final File[] files = getFilesToRelease(file); + if (files == null || files.length == 0) { + if (DEBUG_F2FS) { + Slog.d(TAG, "SKIP; no files to compress"); + } + return; + } + for (int i = files.length - 1; i >= 0; --i) { + final long releasedBlocks = nativeReleaseCompressedBlocks(files[i].getAbsolutePath()); + if (DEBUG_F2FS) { + Slog.d(TAG, "RELEASED " + releasedBlocks + " blocks" + + " from \"" + files[i] + "\""); + } + } + } + + /** + * Returns {@code true} if compression is allowed on the file system containing + * the given file. + * <p> + * NOTE: The return value does not mean if the given file, or any other file + * on the same file system, is actually compressed. It merely determines whether + * not files <em>may</em> be compressed. + */ + private static boolean isCompressionAllowed(@NonNull File file) { + final String filePath; + try { + filePath = file.getCanonicalPath(); + } catch (IOException e) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; could not determine path"); + } + return false; + } + if (IncrementalManager.isIncrementalPath(filePath)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; file on incremental fs"); + } + return false; + } + if (!isChild(sDataDirectory, filePath)) { + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression DISABLED; file not on /data"); + } + return false; + } + if (DEBUG_F2FS) { + Slog.d(TAG, "f2fs compression ENABLED"); + } + return true; + } + + /** + * Returns {@code true} if the given child is a descendant of the base. + */ + private static boolean isChild(@NonNull File base, @NonNull String childPath) { + try { + base = base.getCanonicalFile(); + + File parentFile = new File(childPath).getCanonicalFile(); + while (parentFile != null) { + if (base.equals(parentFile)) { + return true; + } + parentFile = parentFile.getParentFile(); + } + return false; + } catch (IOException ignore) { + return false; + } + } + + /** + * Returns whether or not the compression feature is enabled in the kernel. + * <p> + * NOTE: This doesn't mean compression is enabled on a particular file system + * or any files have been compressed. Only that the functionality is enabled + * on the device. + */ + private static boolean isCompressionEnabledInKernel() { + final File[] features = sKernelFeatures.listFiles(); + if (features == null || features.length == 0) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; no kernel features"); + } + return false; + } + for (int i = features.length - 1; i >= 0; --i) { + final File feature = features[i]; + if (COMPRESSION_FEATURE.equals(features[i].getName())) { + if (DEBUG_F2FS) { + Slog.d(TAG, "FOUND kernel compression feature"); + } + return true; + } + } + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; kernel compression feature not found"); + } + return false; + } + + /** + * Returns whether or not the compression feature is enabled on user data [ie. "/data"]. + * <p> + * NOTE: This doesn't mean any files have been compressed. Only that the functionality + * is enabled on the file system. + */ + private static boolean isCompressionEnabledOnUserData() { + if (!sUserDataFeatures.exists() + || !sUserDataFeatures.isFile() + || !sUserDataFeatures.canRead()) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; filesystem features not available"); + } + return false; + } + final List<String> configLines; + try { + configLines = Files.readAllLines(sUserDataFeatures.toPath()); + } catch (IOException ignore) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; couldn't read filesystem features"); + } + return false; + } + if (configLines == null + || configLines.size() > 1 + || TextUtils.isEmpty(configLines.get(0))) { + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; no filesystem features"); + } + return false; + } + final String[] features = configLines.get(0).split(","); + for (int i = features.length - 1; i >= 0; --i) { + if (COMPRESSION_FEATURE.equals(features[i].trim())) { + if (DEBUG_F2FS) { + Slog.d(TAG, "FOUND filesystem compression feature"); + } + return true; + } + } + if (DEBUG_F2FS) { + Slog.d(TAG, "ERROR; filesystem compression feature not found"); + } + return false; + } + + /** + * Returns all files contained within the directory at any depth from the given path. + */ + private static List<File> getFilesRecursive(@NonNull File path) { + final File[] allFiles = path.listFiles(); + if (allFiles == null) { + return null; + } + final ArrayList<File> files = new ArrayList<>(); + for (File f : allFiles) { + if (f.isDirectory()) { + files.addAll(getFilesRecursive(f)); + } else if (f.isFile()) { + files.add(f); + } + } + return files; + } + + /** + * Returns all files contained within the directory at any depth from the given path. + */ + private static File[] getFilesToRelease(@NonNull File codePath) { + final List<File> files = getFilesRecursive(codePath); + if (files == null) { + if (codePath.isFile()) { + return new File[] { codePath }; + } + return null; + } + if (files.size() == 0) { + return null; + } + return files.toArray(new File[files.size()]); + } + + private static native long nativeReleaseCompressedBlocks(String path); + +} diff --git a/core/java/com/android/internal/content/OWNERS b/core/java/com/android/internal/content/OWNERS new file mode 100644 index 000000000000..c42bee69410d --- /dev/null +++ b/core/java/com/android/internal/content/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 36137 +include /core/java/android/content/pm/OWNERS + +per-file ReferrerIntent.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file ReferrerIntent.java = file:/services/core/java/com/android/server/am/OWNERS diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 68388d98dbb4..125182cab254 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -84,6 +84,7 @@ cc_library_shared { android: { srcs: [ "AndroidRuntime.cpp", + "com_android_internal_content_F2fsUtils.cpp", "com_android_internal_content_NativeLibraryHelper.cpp", "com_google_android_gles_jni_EGLImpl.cpp", "com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 3debb3e03483..443bfce7f050 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -189,6 +189,7 @@ extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_android_security_Scrypt(JNIEnv *env); +extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); @@ -1624,6 +1625,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_security_Scrypt), + REG_JNI(register_com_android_internal_content_F2fsUtils), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_DmabufInfoReader), REG_JNI(register_com_android_internal_os_FuseAppLoop), diff --git a/core/jni/com_android_internal_content_F2fsUtils.cpp b/core/jni/com_android_internal_content_F2fsUtils.cpp new file mode 100644 index 000000000000..8b9d59c416a0 --- /dev/null +++ b/core/jni/com_android_internal_content_F2fsUtils.cpp @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#define LOG_TAG "F2fsUtils" + +#include "core_jni_helpers.h" + +#include <nativehelper/ScopedUtfChars.h> +#include <nativehelper/jni_macros.h> + +#include <sys/ioctl.h> +#include <sys/types.h> + +#include <linux/f2fs.h> +#include <linux/fs.h> + +#include <android-base/unique_fd.h> + +#include <utils/Log.h> + +#include <errno.h> +#include <fcntl.h> + +#include <array> + +using namespace std::literals; + +namespace android { + +static jlong com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks(JNIEnv *env, + jclass clazz, + jstring path) { + unsigned long long blkcnt; + int ret; + ScopedUtfChars filePath(env, path); + + android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC, 0)); + if (fd < 0) { + ALOGW("Failed to open file: %s (%d)\n", filePath.c_str(), errno); + return 0; + } + + long flags = 0; + ret = ioctl(fd, FS_IOC_GETFLAGS, &flags); + if (ret < 0) { + ALOGW("Failed to get flags for file: %s (%d)\n", filePath.c_str(), errno); + return 0; + } + if ((flags & FS_COMPR_FL) == 0) { + return 0; + } + + ret = ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blkcnt); + if (ret < 0) { + return -errno; + } + return blkcnt; +} + +static const std::array gMethods = { + MAKE_JNI_NATIVE_METHOD( + "nativeReleaseCompressedBlocks", "(Ljava/lang/String;)J", + com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks), +}; + +int register_com_android_internal_content_F2fsUtils(JNIEnv *env) { + return RegisterMethodsOrDie(env, "com/android/internal/content/F2fsUtils", gMethods.data(), + gMethods.size()); +} + +}; // namespace android diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ee33d48768c5..f8b9c6b83ada 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4521,6 +4521,9 @@ --> </integer-array> + <!-- How long it takes for the HW to start illuminating after the illumination is requested. --> + <integer name="config_udfps_illumination_transition_ms">50</integer> + <!-- Indicates whether device has a power button fingerprint sensor. --> <bool name="config_is_powerbutton_fps" translatable="false" >false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7d685a202538..7c518983fb83 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2590,6 +2590,7 @@ <java-symbol type="array" name="config_biometric_sensors" /> <java-symbol type="bool" name="allow_test_udfps" /> <java-symbol type="array" name="config_udfps_sensor_props" /> + <java-symbol type="integer" name="config_udfps_illumination_transition_ms" /> <java-symbol type="bool" name="config_is_powerbutton_fps" /> <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" /> diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 0264f60fbd4d..8aba87ba3c8f 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -1100,6 +1100,11 @@ public class RippleDrawable extends LayerDrawable { if (mState.mRippleStyle == STYLE_SOLID) { mMaskCanvas.translate(left, top); } + if (mState.mRippleStyle == STYLE_PATTERNED) { + for (int i = 0; i < mRunningAnimations.size(); i++) { + mRunningAnimations.get(i).getProperties().getShader().setShader(mMaskShader); + } + } } private int getMaskType() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index d749c320bf94..03a90c6d4677 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -191,8 +191,15 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { myUserId())) { return; } + mDisplayLayout.rotateTo(context.getResources(), toRotation); updateDisplayBounds(); + + if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + context.getContentResolver(), myUserId())) { + // If current settings is swipe notification, skip finishOffset. + return; + } finishOffset(0, TRANSITION_DIRECTION_EXIT); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8bfc2c14ad7c..69885df427fb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,16 +174,12 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); if (window) { - int extraBuffers = 0; - native_window_get_extra_buffer_count(window, &extraBuffers); - mNativeSurface = std::make_unique<ReliableSurface>(window); mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(window, 4000_ms); } - mNativeSurface->setExtraBufferCount(extraBuffers); } else { mNativeSurface = nullptr; } diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index c29cc11fa7ea..6df34bee2224 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -278,7 +278,6 @@ int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_query int result = query(window, what, value); if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { std::lock_guard _lock{rs->mMutex}; - *value += rs->mExtraBuffers; rs->mExpectedBufferCount = *value + 2; } return result; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 41969e776fc8..595964741049 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -51,11 +51,6 @@ public: return ret; } - void setExtraBufferCount(size_t extraBuffers) { - std::lock_guard _lock{mMutex}; - mExtraBuffers = extraBuffers; - } - bool didSetExtraBuffers() const { std::lock_guard _lock{mMutex}; return mDidSetExtraBuffers; @@ -73,7 +68,6 @@ private: base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; int mBufferQueueState = OK; - size_t mExtraBuffers = 0; size_t mExpectedBufferCount = 0; bool mDidSetExtraBuffers = false; diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index f4466782ac8a..4cd3616566c2 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -363,9 +363,9 @@ public final class GnssMeasurement implements Parcelable { } /** - * Gets per-satellite sync state. + * Gets per-satellite-signal sync state. * - * <p>It represents the current sync state for the associated satellite. + * <p>It represents the current sync state for the associated satellite signal. * * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}. */ diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml index 904b78c7f165..6f504efbaaf0 100644 --- a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml +++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml @@ -15,7 +15,7 @@ limitations under the License. --> -<LinearLayout +<com.android.settingslib.widget.BannerMessageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -23,6 +23,7 @@ style="@style/Banner.Preference.SettingsLib"> <RelativeLayout + android:id="@+id/top_row" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="8dp" @@ -49,6 +50,7 @@ android:id="@+id/banner_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="0dp" android:paddingBottom="4dp" android:textAppearance="@style/Banner.Title.SettingsLib"/> @@ -56,6 +58,7 @@ android:id="@+id/banner_subtitle" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="0dp" android:paddingBottom="4dp" android:textAppearance="@style/Banner.Subtitle.SettingsLib" android:visibility="gone"/> @@ -87,4 +90,4 @@ android:layout_height="wrap_content" style="@style/Banner.ButtonText.SettingsLib"/> </LinearLayout> -</LinearLayout>
\ No newline at end of file +</com.android.settingslib.widget.BannerMessageView>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java new file mode 100644 index 000000000000..5ca6fb64db2d --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java @@ -0,0 +1,111 @@ +/* + * 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.settingslib.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.TouchDelegate; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +/** + * The view providing {@link BannerMessagePreference}. + * + * <p>Callers should not instantiate this view directly but rather through adding a + * {@link BannerMessagePreference} to a {@code PreferenceScreen}. + */ +public class BannerMessageView extends LinearLayout { + private Rect mTouchTargetForDismissButton; + + public BannerMessageView(Context context) { + super(context); + } + + public BannerMessageView(Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BannerMessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BannerMessageView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setupIncreaseTouchTargetForDismissButton(); + } + + private void setupIncreaseTouchTargetForDismissButton() { + if (mTouchTargetForDismissButton != null) { + // Already set up + return; + } + + // The dismiss button is in the 'top row' RelativeLayout for positioning, but this element + // does not have enough space to provide large touch targets. We therefore set the top + // target on this view. + View topRow = findViewById(R.id.top_row); + View dismissButton = findViewById(R.id.banner_dismiss_btn); + if (topRow == null || dismissButton == null || dismissButton.getVisibility() != VISIBLE) { + return; + } + + int minimum = + getResources() + .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target); + int width = dismissButton.getWidth(); + int height = dismissButton.getHeight(); + int widthIncrease = width < minimum ? minimum - width : 0; + int heightIncrease = height < minimum ? minimum - height : 0; + + // Compute the hit rect of dismissButton within the local co-orindate reference of this view + // (rather than it's direct parent topRow). + Rect hitRectWithinTopRow = new Rect(); + dismissButton.getHitRect(hitRectWithinTopRow); + Rect hitRectOfTopRowWithinThis = new Rect(); + topRow.getHitRect(hitRectOfTopRowWithinThis); + mTouchTargetForDismissButton = new Rect(); + mTouchTargetForDismissButton.left = + hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.left; + mTouchTargetForDismissButton.right = + hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.right; + mTouchTargetForDismissButton.top = + hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.top; + mTouchTargetForDismissButton.bottom = + hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.bottom; + + // Adjust the touch target rect to apply the necessary increase in width and height. + mTouchTargetForDismissButton.left -= + widthIncrease % 2 == 1 ? (widthIncrease / 2) + 1 : widthIncrease / 2; + mTouchTargetForDismissButton.top -= + heightIncrease % 2 == 1 ? (heightIncrease / 2) + 1 : heightIncrease / 2; + mTouchTargetForDismissButton.right += widthIncrease / 2; + mTouchTargetForDismissButton.bottom += heightIncrease / 2; + + setTouchDelegate(new TouchDelegate(mTouchTargetForDismissButton, dismissButton)); + } + +} diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index acbf35946126..ddcc83eee4bf 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -18,4 +18,5 @@ <resources> <dimen name="app_preference_padding_start">20dp</dimen> <dimen name="app_icon_min_width">52dp</dimen> + <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen> </resources> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java index 6670ed307816..0a48f19a3021 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java @@ -20,7 +20,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.robolectric.Robolectric.setupActivity; +import static org.robolectric.Shadows.shadowOf; +import android.app.Activity; import android.content.Context; import android.graphics.ColorFilter; import android.graphics.PorterDuff; @@ -44,8 +47,8 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.shadows.ShadowDrawable; +import org.robolectric.shadows.ShadowTouchDelegate; import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @@ -58,6 +61,9 @@ public class BannerMessagePreferenceTest { private boolean mClickListenerCalled = false; private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true; + private final int mMinimumTargetSize = + RuntimeEnvironment.application.getResources() + .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target); private static final int TEST_STRING_RES_ID = R.string.accessibility_banner_message_dismiss; @@ -81,6 +87,23 @@ public class BannerMessagePreferenceTest { } @Test + public void onBindViewHolder_andOnLayoutView_dismissButtonTouchDelegate_isCorrectSize() { + assumeAndroidS(); + mBannerPreference.setTitle("Title"); + mBannerPreference.setDismissButtonOnClickListener(mClickListener); + + mBannerPreference.onBindViewHolder(mHolder); + setupActivity(Activity.class).setContentView(mRootView); + + assertThat(mRootView.getTouchDelegate()).isNotNull(); + ShadowTouchDelegate delegate = shadowOf(mRootView.getTouchDelegate()); + assertThat(delegate.getBounds().width()).isAtLeast(mMinimumTargetSize); + assertThat(delegate.getBounds().height()).isAtLeast(mMinimumTargetSize); + assertThat(delegate.getDelegateView()) + .isEqualTo(mRootView.findViewById(R.id.banner_dismiss_btn)); + } + + @Test public void onBindViewHolder_whenSummarySet_shouldSetSummary() { mBannerPreference.setSummary("test"); @@ -157,7 +180,7 @@ public class BannerMessagePreferenceTest { mBannerPreference.onBindViewHolder(mHolder); ImageView mIcon = mRootView.findViewById(R.id.banner_icon); - ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable()); assertThat(shadowDrawable.getCreatedFromResId()) .isEqualTo(R.drawable.settingslib_ic_cross); } @@ -168,7 +191,7 @@ public class BannerMessagePreferenceTest { mBannerPreference.onBindViewHolder(mHolder); ImageView mIcon = mRootView.findViewById(R.id.banner_icon); - ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable()); + ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable()); assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_warning); } @@ -478,11 +501,15 @@ public class BannerMessagePreferenceTest { private void assumeAndroidR() { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 30); + ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "R"); + ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", false); // Reset view holder to use correct layout. } private void assumeAndroidS() { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 31); + ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "S"); + ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", true); // Re-inflate view to update layout. setUpViewHolder(); } diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 82ce881b2fbc..9678affa96a7 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -426,7 +426,7 @@ <!-- Whether or not the dividing lines should be shown when the container is expanding and collapsing. If this value is true, then the lines will only show when the container has been completely expanded. --> - <bool name="config_hideDividersDuringExpand">false</bool> + <bool name="config_hideDividersDuringExpand">true</bool> <!-- Whether or not child notifications that are part of a group will have shadows. --> <bool name="config_enableShadowOnChildNotifications">true</bool> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java index 6a6f57a64be6..f8be35ab6cd8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java @@ -55,6 +55,7 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { @NonNull private final SurfaceHolder mHolder; @NonNull private final Paint mSensorPaint; @NonNull private final SimpleDrawable mIlluminationDotDrawable; + private final int mOnIlluminatedDelayMs; private final @HbmType int mHbmType; @NonNull private RectF mSensorRect; @@ -82,6 +83,9 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { canvas.drawOval(mSensorRect, mSensorPaint); }; + mOnIlluminatedDelayMs = mContext.getResources().getInteger( + com.android.internal.R.integer.config_udfps_illumination_transition_ms); + if (Build.IS_ENG || Build.IS_USERDEBUG) { mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(), SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT); @@ -107,9 +111,8 @@ public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator { } if (onIlluminatedRunnable != null) { // No framework API can reliably tell when a frame reaches the panel. A timeout - // is the safest solution. The frame should be displayed within 3 refresh - // cycles, which on a 60 Hz panel equates to 50 milliseconds. - postDelayed(onIlluminatedRunnable, 50 /* delayMillis */); + // is the safest solution. + postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); } else { Log.w(TAG, "startIllumination | onIlluminatedRunnable is null"); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index 2e03d9a90f49..6f878d19aa4b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -67,7 +67,7 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe @Nullable private final IWallpaperManager mWallpaperManagerService; - private int mWakefulness = WAKEFULNESS_ASLEEP; + private int mWakefulness = WAKEFULNESS_AWAKE; private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 5efa1b2992b2..aea8849adc24 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -573,6 +573,10 @@ public class ScreenshotController { mScreenshotView.updateDisplayCutoutMargins( mWindowManager.getCurrentWindowMetrics().getWindowInsets() .getDisplayCutout()); + // screenshot animation calculations won't be valid anymore, so just end + if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { + mScreenshotAnimation.end(); + } } }); }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 6822d24947c3..26606cda5582 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -224,8 +224,14 @@ public class AmbientState { return mScrollY; } + /** + * Set the new Scroll Y position. + */ public void setScrollY(int scrollY) { - this.mScrollY = scrollY; + // Because we're dealing with an overscroller, scrollY could sometimes become smaller than + // 0. However this is only for internal purposes and the scroll position when read + // should never be smaller than 0, otherwise it can lead to flickers. + this.mScrollY = Math.max(scrollY, 0); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 99fe541d0135..f2e39e78df36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -710,10 +710,10 @@ public class NotificationChildrenContainer extends ViewGroup { if (mUserLocked) { expandFraction = getGroupExpandFraction(); } - final boolean dividersVisible = mUserLocked && !showingAsLowPriority() - || (mChildrenExpanded && mShowDividersWhenExpanded) - || (mContainingNotification.isGroupExpansionChanging() - && !mHideDividersDuringExpand); + final boolean isExpanding = !showingAsLowPriority() + && (mUserLocked || mContainingNotification.isGroupExpansionChanging()); + final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded) + || (isExpanding && !mHideDividersDuringExpand); for (int i = 0; i < childCount; i++) { ExpandableNotificationRow child = mAttachedChildren.get(i); ExpandableViewState viewState = child.getViewState(); @@ -789,10 +789,10 @@ public class NotificationChildrenContainer extends ViewGroup { int childCount = mAttachedChildren.size(); ViewState tmpState = new ViewState(); float expandFraction = getGroupExpandFraction(); - final boolean dividersVisible = mUserLocked && !showingAsLowPriority() - || (mChildrenExpanded && mShowDividersWhenExpanded) - || (mContainingNotification.isGroupExpansionChanging() - && !mHideDividersDuringExpand); + final boolean isExpanding = !showingAsLowPriority() + && (mUserLocked || mContainingNotification.isGroupExpansionChanging()); + final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded) + || (isExpanding && !mHideDividersDuringExpand); for (int i = childCount - 1; i >= 0; i--) { ExpandableNotificationRow child = mAttachedChildren.get(i); ExpandableViewState viewState = child.getViewState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index b72ac7421176..f0201cb3482d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1185,7 +1185,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void updateStackPosition(boolean listenerNeedsAnimation) { // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition - + mAmbientState.getOverExpansion(); + + mAmbientState.getOverExpansion() + - getCurrentOverScrollAmount(false /* top */); final float fraction = mAmbientState.getExpansionFraction(); final float stackY = MathUtils.lerp(0, endTopPosition, fraction); mAmbientState.setStackY(stackY); @@ -1907,6 +1908,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (onTop) { notifyOverscrollTopListener(amount, isRubberbanded); } + updateStackPosition(); requestChildrenUpdate(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index fb4f5592e97f..495eda772219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1071,10 +1071,6 @@ public class NotificationStackScrollLayoutController { mView.setAlpha(alpha); } - public float getCurrentOverScrollAmount(boolean top) { - return mView.getCurrentOverScrollAmount(top); - } - public float calculateAppearFraction(float height) { return mView.calculateAppearFraction(height); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 74e8de4c9d11..8f4a71cf2563 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -218,13 +218,7 @@ public class StackScrollAlgorithm { */ private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state, AmbientState ambientState) { - float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */); - int scrollY = ambientState.getScrollY(); - - // Due to the overScroller, the stackscroller can have negative scroll state. This is - // already accounted for by the top padding and doesn't need an additional adaption - scrollY = Math.max(0, scrollY); - state.scrollY = (int) (scrollY + bottomOverScroll); + state.scrollY = ambientState.getScrollY(); state.mCurrentYPosition = -state.scrollY; state.mCurrentExpandedYPosition = -state.scrollY; @@ -261,7 +255,7 @@ public class StackScrollAlgorithm { // Save the index of first view in shelf from when shade is fully // expanded. Consider updating these states in updateContentView instead so that we don't // have to recalculate in every frame. - float currentY = -scrollY; + float currentY = -ambientState.getScrollY(); if (!ambientState.isOnKeyguard()) { currentY += mNotificationScrimPadding; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index efeeac669491..e6c4e82cec7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -336,7 +336,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, results); - mEntry.remoteInputText = mEditText.getText(); + mEntry.remoteInputText = mEditText.getText().toString(); // TODO(b/188646667): store attachment to entry mEntry.remoteInputUri = null; mEntry.remoteInputMimeType = null; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a5ccc47b8072..ab4b1f10132c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -135,7 +135,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean mShowVolumeDialog; private boolean mShowSafetyWarning; private long mLastToggledRingerOn; - private boolean mDeviceInteractive; + private boolean mDeviceInteractive = true; private boolean mDestroyed; private VolumePolicy mVolumePolicy; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java index 63ce98a170a6..910b38105332 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java @@ -56,7 +56,7 @@ public class WakefulnessLifecycleTest extends SysuiTestCase { @Test public void baseState() throws Exception { - assertEquals(WakefulnessLifecycle.WAKEFULNESS_ASLEEP, mWakefulness.getWakefulness()); + assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness()); verifyNoMoreInteractions(mWakefulnessObserver); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 7f6e6687625c..08f6f1e6e46e 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; @@ -2524,6 +2525,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public CellularBatteryStats getCellularBatteryStats() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); + } + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { @@ -2536,6 +2543,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public WifiBatteryStats getWifiBatteryStats() { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.BATTERY_STATS, null); + } + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { @@ -2548,6 +2561,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub * @hide */ public GpsBatteryStats getGpsBatteryStats() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null); + // Wait for the completion of pending works if there is any awaitCompletion(); synchronized (mStats) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 8a8b74e40568..184ddae9699d 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -803,7 +803,7 @@ public class DisplayDeviceConfig { + ", transition: " + transitionPoint + ", timeWindow: " + timeWindowMillis + "ms" + ", timeMax: " + timeMaxMillis + "ms" - + ", timeMin: " + timeMinMillis + + ", timeMin: " + timeMinMillis + "ms" + "} "; } } diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index b9487779dfd3..57a8c4b998ae 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -22,6 +22,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import android.util.TimeUtils; import android.view.SurfaceControlHdrLayerInfoListener; import com.android.internal.annotations.VisibleForTesting; @@ -189,6 +190,10 @@ class HighBrightnessModeController { } void dump(PrintWriter pw) { + mHandler.runWithScissors(() -> dumpLocal(pw), 1000); + } + + private void dumpLocal(PrintWriter pw) { pw.println("HighBrightnessModeController:"); pw.println(" mCurrentMin=" + getCurrentBrightnessMin()); pw.println(" mCurrentMax=" + getCurrentBrightnessMax()); @@ -202,6 +207,29 @@ class HighBrightnessModeController { pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent); pw.println(" mBrightnessMin=" + mBrightnessMin); pw.println(" mBrightnessMax=" + mBrightnessMax); + pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis)); + pw.println(" mEvents="); + final long currentTime = mClock.uptimeMillis(); + long lastStartTime = currentTime; + if (mRunningStartTimeMillis != -1) { + lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime)); + } + for (HbmEvent event : mEvents) { + if (lastStartTime > event.endTimeMillis) { + pw.println(" event: [normal brightness]: " + + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis)); + } + lastStartTime = dumpHbmEvent(pw, event); + } + } + + private long dumpHbmEvent(PrintWriter pw, HbmEvent event) { + final long duration = event.endTimeMillis - event.startTimeMillis; + pw.println(" event: [" + + TimeUtils.formatUptime(event.startTimeMillis) + ", " + + TimeUtils.formatUptime(event.endTimeMillis) + "] (" + + TimeUtils.formatDuration(duration) + ")"); + return event.startTimeMillis; } private boolean isCurrentlyAllowed() { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2ed160a33af4..a086bdaa6ec9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -2580,7 +2580,8 @@ public class HdmiControlService extends SystemService { return null; } - private void addHdmiControlStatusChangeListener( + @VisibleForTesting + void addHdmiControlStatusChangeListener( final IHdmiControlStatusChangeListener listener) { final HdmiControlStatusChangeListenerRecord record = new HdmiControlStatusChangeListenerRecord(listener); @@ -2916,13 +2917,17 @@ public class HdmiControlService extends SystemService { } else { mIsCecAvailable = true; } + if (!listeners.isEmpty()) { + invokeHdmiControlStatusChangeListenerLocked(listeners, + isEnabled, mIsCecAvailable); + } } }); } else { mIsCecAvailable = false; - } - if (!listeners.isEmpty()) { - invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable); + if (!listeners.isEmpty()) { + invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable); + } } } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index cd383b9d1d7a..8fd545f271f3 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -80,6 +80,17 @@ public class Installer extends SystemService { /** Indicates that dexopt may be run with different performance / priority tuned for restore */ public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove + /** The result of the profile analysis indicating that the app should be optimized. */ + public static final int PROFILE_ANALYSIS_OPTIMIZE = 1; + /** The result of the profile analysis indicating that the app should not be optimized. */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA = 2; + /** + * The result of the profile analysis indicating that the app should not be optimized because + * the profiles are empty. + */ + public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; + + public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; @@ -496,9 +507,18 @@ public class Installer extends SystemService { } } - public boolean mergeProfiles(int uid, String packageName, String profileName) + /** + * Analyzes the ART profiles of the given package, possibly merging the information + * into the reference profile. Returns whether or not we should optimize the package + * based on how much information is in the profile. + * + * @return one of {@link #PROFILE_ANALYSIS_OPTIMIZE}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA}, + * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES} + */ + public int mergeProfiles(int uid, String packageName, String profileName) throws InstallerException { - if (!checkBeforeRemote()) return false; + if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; try { return mInstalld.mergeProfiles(uid, packageName, profileName); } catch (Exception e) { @@ -645,13 +665,17 @@ public class Installer extends SystemService { } } - public void deleteOdex(String apkPath, String instructionSet, String outputPath) + /** + * Deletes the optimized artifacts generated by ART and returns the number + * of freed bytes. + */ + public long deleteOdex(String apkPath, String instructionSet, String outputPath) throws InstallerException { - if (!checkBeforeRemote()) return; + if (!checkBeforeRemote()) return -1; BlockGuard.getVmPolicy().onPathAccess(apkPath); BlockGuard.getVmPolicy().onPathAccess(outputPath); try { - mInstalld.deleteOdex(apkPath, instructionSet, outputPath); + return mInstalld.deleteOdex(apkPath, instructionSet, outputPath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 131539e82af6..5fd8e3c6e302 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -31,6 +31,9 @@ import static com.android.server.pm.Installer.DEXOPT_PUBLIC; import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX; import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE; import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; +import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; @@ -248,8 +251,12 @@ public class PackageDexOptimizer { || packageUseInfo.isUsedByOtherApps(path); final String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter(), isUsedByOtherApps); - final boolean profileUpdated = options.isCheckForProfileUpdates() && - isProfileUpdated(pkg, sharedGid, profileName, compilerFilter); + // If we don't have to check for profiles updates assume + // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to + // profiles. + final int profileAnalysisResult = options.isCheckForProfileUpdates() + ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter) + : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct // flags. @@ -257,7 +264,7 @@ public class PackageDexOptimizer { for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter, - profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid, + profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid, packageStats, options.isDowngrade(), profileName, dexMetadataPath, options.getCompilationReason()); @@ -306,11 +313,11 @@ public class PackageDexOptimizer { */ @GuardedBy("mInstallLock") private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path, - String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext, + String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason) { int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, - profileUpdated, downgrade); + profileAnalysisResult, downgrade); if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { return DEX_OPT_SKIPPED; } @@ -364,7 +371,7 @@ public class PackageDexOptimizer { isa, options.getCompilerFilter(), dexUseInfo.getClassLoaderContext(), - /* newProfile= */false, + PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES, /* downgrade= */ false); if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { @@ -750,11 +757,25 @@ public class PackageDexOptimizer { * configuration (isa, compiler filter, profile). */ private int getDexoptNeeded(String path, String isa, String compilerFilter, - String classLoaderContext, boolean newProfile, boolean downgrade) { + String classLoaderContext, int profileAnalysisResult, boolean downgrade) { int dexoptNeeded; try { - dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext, - newProfile, downgrade); + // A profile guided optimizations with an empty profile is essentially 'verify' and + // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot + // check the profiles because system server does not have access to them. + // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and + // manually adjust the actual filter before checking. + // + // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change, + // and in the interim we can still improve things here. + String actualCompilerFilter = compilerFilter; + if (compilerFilterDependsOnProfiles(compilerFilter) + && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) { + actualCompilerFilter = "verify"; + } + boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE; + dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter, + classLoaderContext, newProfile, downgrade); } catch (IOException ioe) { Slog.w(TAG, "IOException reading apk: " + path, ioe); return DEX_OPT_FAILED; @@ -765,27 +786,34 @@ public class PackageDexOptimizer { return adjustDexoptNeeded(dexoptNeeded); } + /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */ + private boolean compilerFilterDependsOnProfiles(String compilerFilter) { + return compilerFilter.endsWith("-profile"); + } + /** * Checks if there is an update on the profile information of the {@code pkg}. - * If the compiler filter is not profile guided the method returns false. + * If the compiler filter is not profile guided the method returns a safe default: + * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA. * * Note that this is a "destructive" operation with side effects. Under the hood the * current profile and the reference profile will be merged and subsequent calls * may return a different result. */ - private boolean isProfileUpdated(AndroidPackage pkg, int uid, String profileName, + private int analyseProfiles(AndroidPackage pkg, int uid, String profileName, String compilerFilter) { // Check if we are allowed to merge and if the compiler filter is profile guided. if (!isProfileGuidedCompilerFilter(compilerFilter)) { - return false; + return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; } // Merge profiles. It returns whether or not there was an updated in the profile info. try { return mInstaller.mergeProfiles(uid, pkg.getPackageName(), profileName); } catch (InstallerException e) { Slog.w(TAG, "Failed to merge profiles", e); + // We don't need to optimize if we failed to merge. + return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; } - return false; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a8cc5fdf884d..5817a80ec6c1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -341,6 +341,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; +import com.android.internal.content.F2fsUtils; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.content.om.OverlayConfig; @@ -896,6 +897,20 @@ public class PackageManagerService extends IPackageManager.Stub * Only non-null during an OTA, and even then it is nulled again once systemReady(). */ private @Nullable ArraySet<String> mExistingPackages = null; + + /** + * List of code paths that need to be released when the system becomes ready. + * <p> + * NOTE: We have to delay releasing cblocks for no other reason than we cannot + * retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. When + * we no longer need to read that setting, cblock release can occur in the + * constructor. + * + * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL + * @see #systemReady() + */ + private @Nullable List<File> mReleaseOnSystemReady; + /** * Whether or not system app permissions should be promoted from install to runtime. */ @@ -7966,6 +7981,21 @@ public class PackageManagerService extends IPackageManager.Stub IoUtils.closeQuietly(handle); } } + if (ret == PackageManager.INSTALL_SUCCEEDED) { + // NOTE: During boot, we have to delay releasing cblocks for no other reason than + // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. + // When we no longer need to read that setting, cblock release can occur always + // occur here directly + if (!mSystemReady) { + if (mReleaseOnSystemReady == null) { + mReleaseOnSystemReady = new ArrayList<>(); + } + mReleaseOnSystemReady.add(dstCodePath); + } else { + final ContentResolver resolver = mContext.getContentResolver(); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + } if (ret != PackageManager.INSTALL_SUCCEEDED) { if (!dstCodePath.exists()) { return null; @@ -17853,6 +17883,10 @@ public class PackageManagerService extends IPackageManager.Stub if (mRet == PackageManager.INSTALL_SUCCEEDED) { mRet = args.copyApk(); } + if (mRet == PackageManager.INSTALL_SUCCEEDED) { + F2fsUtils.releaseCompressedBlocks( + mContext.getContentResolver(), new File(args.getCodePath())); + } if (mParentInstallParams != null) { mParentInstallParams.tryProcessInstallRequest(args, mRet); } else { @@ -17860,7 +17894,6 @@ public class PackageManagerService extends IPackageManager.Stub processInstallRequestsAsync( res.returnCode == PackageManager.INSTALL_SUCCEEDED, Collections.singletonList(new InstallRequest(args, res))); - } } } @@ -24235,8 +24268,15 @@ public class PackageManagerService extends IPackageManager.Stub public void systemReady() { enforceSystemOrRoot("Only the system can claim the system is ready"); - mSystemReady = true; final ContentResolver resolver = mContext.getContentResolver(); + if (mReleaseOnSystemReady != null) { + for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) { + final File dstCodePath = mReleaseOnSystemReady.get(i); + F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath); + } + mReleaseOnSystemReady = null; + } + mSystemReady = true; ContentObserver co = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { @@ -28391,14 +28431,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - void deleteOatArtifactsOfPackage(String packageName) { + long deleteOatArtifactsOfPackage(String packageName) { final AndroidPackage pkg; final PackageSetting pkgSetting; synchronized (mLock) { pkg = mPackages.get(packageName); pkgSetting = mSettings.getPackageLPr(packageName); } - mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); + return mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); } Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e8897cab14ff..d4feb3a728c8 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3902,6 +3902,17 @@ public class UserManagerService extends IUserManager.Stub { isFirstBoot, isUpgrade, existingPackages); } + @Override + public String[] getPreInstallableSystemPackages(@NonNull String userType) { + checkManageOrCreateUsersPermission("getPreInstallableSystemPackages"); + final Set<String> installableSystemPackages = + mSystemPackageInstaller.getInstallablePackagesForUserType(userType); + if (installableSystemPackages == null) { + return null; + } + return installableSystemPackages.toArray(new String[installableSystemPackages.size()]); + } + private long getCreationTime() { final long now = System.currentTimeMillis(); return (now > EPOCH_PLUS_30_YEARS) ? now : 0; 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 32ba26c2d5ed..58204891293c 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -1034,18 +1034,26 @@ public class DexManager { /** * Deletes all the optimizations files generated by ART. + * This is best effort, and the method will log but not throw errors + * for individual deletes + * * @param packageInfo the package information. + * @return the number of freed bytes or -1 if there was an error in the process. */ - public void deleteOptimizedFiles(ArtPackageInfo packageInfo) { + public long deleteOptimizedFiles(ArtPackageInfo packageInfo) { + long freedBytes = 0; + boolean hadErrors = false; for (String codePath : packageInfo.getCodePaths()) { for (String isa : packageInfo.getInstructionSets()) { try { - mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); + freedBytes += mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); } catch (InstallerException e) { Log.e(TAG, "Failed deleting oat files for " + codePath, e); + hadErrors = true; } } } + return hadErrors ? -1 : freedBytes; } public static class RegisterDexModuleResult { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8514f3599832..542ec809c484 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4593,7 +4593,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (visible) { displayContent.mOpeningApps.add(this); mEnteringAnimation = true; - } else { + } else if (mVisible) { displayContent.mClosingApps.add(this); mEnteringAnimation = false; } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 0cf212c20c1e..f53ae525eaba 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -40,6 +40,7 @@ import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; +import android.hardware.hdmi.IHdmiControlStatusChangeListener; import android.os.Binder; import android.os.IPowerManager; import android.os.IThermalService; @@ -683,6 +684,100 @@ public class HdmiControlServiceTest { HdmiControlManager.HDMI_CEC_VERSION_2_0); } + @Test + public void initCec_statusListener_CecDisabled() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isFalse(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse(); + } + + @Test + public void initCec_statusListener_CecEnabled_NoCecResponse() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mTestLooper.dispatchAll(); + // Hit timeout twice due to retries + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse(); + } + + @Test + public void initCec_statusListener_CecEnabled_CecAvailable_TvOn() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, + mHdmiControlServiceSpy.playback().mAddress, HdmiControlManager.POWER_STATUS_ON); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue(); + } + + @Test + public void initCec_statusListener_CecEnabled_CecAvailable_TvStandby() { + HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback(); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback); + mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_TV, + mHdmiControlServiceSpy.playback().mAddress, + HdmiControlManager.POWER_STATUS_STANDBY); + mNativeWrapper.onCecMessage(reportPowerStatus); + mTestLooper.dispatchAll(); + + assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue(); + assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue(); + } + + private static class HdmiControlStatusCallback extends IHdmiControlStatusChangeListener.Stub { + boolean mCecEnabled = false; + boolean mCecAvailable = false; + + @Override + public void onStatusChange(int isCecEnabled, boolean isCecAvailable) + throws RemoteException { + mCecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; + mCecAvailable = isCecAvailable; + } + } + private static class VolumeControlFeatureCallback extends IHdmiCecVolumeControlFeatureListener.Stub { boolean mCallbackReceived = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 821683043804..b7713a9338de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2699,6 +2699,85 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse("Starting window should not be present", activity.hasStartingWindow()); } + @Test + public void testSetVisibility_visibleToVisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).build(); + // By default, activity is visible. + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be visible. Although the activity is already visible, app + // transition animation should be applied on this activity. This might be unnecessary, but + // until we verify no logic relies on this behavior, we'll keep this as is. + activity.setVisibility(true); + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_visibleToInvisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).build(); + // By default, activity is visible. + assertTrue(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be invisible. Since the visibility changes, app transition + // animation should be applied on this activity. + activity.setVisibility(false); + assertTrue(activity.isVisible()); + assertFalse(activity.mVisibleRequested); + assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertTrue(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_invisibleToVisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).setVisible(false).build(); + // Activiby is invisible. However ATMS requests it to become visible, since this is a top + // activity. + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be visible. Since the visibility changes, app transition + // animation should be applied on this activity. + activity.setVisibility(true); + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + + @Test + public void testSetVisibility_invisibleToInvisible() { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true).setVisible(false).build(); + // Activiby is invisible. However ATMS requests it to become visible, since this is a top + // activity. + assertFalse(activity.isVisible()); + assertTrue(activity.mVisibleRequested); + assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + + // Request the activity to be invisible. Since the activity is already invisible, no app + // transition should be applied on this activity. + activity.setVisibility(false); + assertFalse(activity.isVisible()); + assertFalse(activity.mVisibleRequested); + assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); + assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); + } + private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); 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 111449d2c100..588089996d6c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -805,6 +805,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private Bundle mIntentExtras; private boolean mOnTop = false; private ActivityInfo.WindowLayout mWindowLayout; + private boolean mVisible = true; ActivityBuilder(ActivityTaskManagerService service) { mService = service; @@ -930,6 +931,11 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setVisible(boolean visible) { + mVisible = visible; + return this; + } + ActivityRecord build() { SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock); try { @@ -1012,9 +1018,10 @@ class WindowTestsBase extends SystemServiceTestsBase { // root tasks (e.g. home root task). mTask.moveToFront("createActivity"); } - // Make visible by default... - activity.mVisibleRequested = true; - activity.setVisible(true); + if (mVisible) { + activity.mVisibleRequested = true; + activity.setVisible(true); + } } final WindowProcessController wpc; |