diff options
192 files changed, 6032 insertions, 1709 deletions
diff --git a/.gitignore b/.gitignore index c47cc8bf2538..5018436798d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea *.iml *.sw* -gen/
\ No newline at end of file +gen/ +.vscode/ +*.code-workspace diff --git a/Android.bp b/Android.bp index 35f97ac57281..5c0dd63ac0f7 100644 --- a/Android.bp +++ b/Android.bp @@ -532,6 +532,7 @@ java_library { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.security.apc-java", + "android.security.authorization-java", "android.system.keystore2-java", "android.system.suspend.control.internal-java", "cameraprotosnano", diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS index a060ad9a5a7e..7e7feafdc5a5 100644 --- a/apct-tests/perftests/OWNERS +++ b/apct-tests/perftests/OWNERS @@ -1,2 +1,11 @@ -timmurray@google.com +balejs@google.com +carmenjackson@google.com +cfijalkovich@google.com +dualli@google.com +edgararriaga@google.com +jpakaravoor@google.com +kevinjeon@google.com philipcuadra@google.com +shombert@google.com +timmurray@google.com +wessam@google.com diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index 18643ed91276..444164390550 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -16,6 +16,8 @@ package com.android.server; +import android.app.BroadcastOptions; + import com.android.server.deviceidle.IDeviceIdleConstraint; public interface DeviceIdleInternal { @@ -32,8 +34,17 @@ public interface DeviceIdleInternal { void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason); - // duration in milliseconds - void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync, + /** + * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp + * allowlist. + * @param uid + * @param duration duration in milliseconds + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType} + * @param sync + * @param reason + */ + void addPowerSaveTempWhitelistAppDirect(int uid, long duration, + @BroadcastOptions.TempAllowListType int type, boolean sync, String reason); // duration in milliseconds diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 4b98c32db608..7aed32c16ae2 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -21,6 +21,8 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; +import android.app.BroadcastOptions; +import android.app.BroadcastOptions.TempAllowListType; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -1941,9 +1943,9 @@ public class DeviceIdleController extends SystemService // duration in milliseconds @Override - public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync, - String reason) { - addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, sync, reason); + public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, + @TempAllowListType int type, boolean sync, String reason) { + addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason); } // duration in milliseconds @@ -2719,7 +2721,9 @@ public class DeviceIdleController extends SystemService long duration, int userId, boolean sync, String reason) { try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); - addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, sync, reason); + addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, + BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, + reason); } catch (NameNotFoundException e) { } } @@ -2729,7 +2733,7 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid, - long duration, boolean sync, String reason) { + long duration, @TempAllowListType int type, boolean sync, String reason) { final long timeNow = SystemClock.elapsedRealtime(); boolean informWhitelistChanged = false; int appId = UserHandle.getAppId(uid); @@ -2761,7 +2765,7 @@ public class DeviceIdleController extends SystemService } catch (RemoteException e) { } postTempActiveTimeoutMessage(uid, duration); - updateTempWhitelistAppIdsLocked(appId, true); + updateTempWhitelistAppIdsLocked(uid, true, duration, type); if (sync) { informWhitelistChanged = true; } else { @@ -2786,8 +2790,7 @@ public class DeviceIdleController extends SystemService try { final int uid = getContext().getPackageManager().getPackageUidAsUser( packageName, userId); - final int appId = UserHandle.getAppId(uid); - removePowerSaveTempWhitelistAppDirectInternal(appId); + removePowerSaveTempWhitelistAppDirectInternal(uid); } catch (NameNotFoundException e) { } } @@ -2821,7 +2824,8 @@ public class DeviceIdleController extends SystemService Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow); } synchronized (this) { - Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId); + Pair<MutableLong, String> entry = + mTempWhitelistAppIdEndTimes.get(appId); if (entry == null) { // Nothing to do return; @@ -2832,7 +2836,7 @@ public class DeviceIdleController extends SystemService } else { // Need more time if (DEBUG) { - Slog.d(TAG, "Time to remove AppId " + appId + ": " + entry.first.value); + Slog.d(TAG, "Time to remove uid " + uid + ": " + entry.first.value); } postTempActiveTimeoutMessage(uid, entry.first.value - timeNow); } @@ -2841,12 +2845,12 @@ public class DeviceIdleController extends SystemService @GuardedBy("this") private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) { - final int appId = UserHandle.getAppId(uid); if (DEBUG) { Slog.d(TAG, "Removing uid " + uid + " from temp whitelist"); } - updateTempWhitelistAppIdsLocked(appId, false); - mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0) + final int appId = UserHandle.getAppId(uid); + updateTempWhitelistAppIdsLocked(uid, false, 0, 0); + mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0) .sendToTarget(); reportTempWhitelistChangedLocked(uid, false); try { @@ -3869,7 +3873,16 @@ public class DeviceIdleController extends SystemService passWhiteListsToForceAppStandbyTrackerLocked(); } - private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) { + /** + * update temp allowlist. + * @param uid uid to add or remove from temp allowlist. + * @param adding true to add to temp allowlist, false to remove from temp allowlist. + * @param durationMs duration in milliseconds to add to temp allowlist, only valid when + * param adding is true. + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType} + */ + private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs, + @TempAllowListType int type) { final int size = mTempWhitelistAppIdEndTimes.size(); if (mTempWhitelistAppIdArray.length != size) { mTempWhitelistAppIdArray = new int[size]; @@ -3882,8 +3895,8 @@ public class DeviceIdleController extends SystemService Slog.d(TAG, "Setting activity manager temp whitelist to " + Arrays.toString(mTempWhitelistAppIdArray)); } - mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, appId, - adding); + mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid, + adding, durationMs, type); } if (mLocalPowerManager != null) { if (DEBUG) { diff --git a/core/api/current.txt b/core/api/current.txt index 113591e5e97b..27660ec88f44 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1055,11 +1055,11 @@ package android { field public static final int parentActivityName = 16843687; // 0x10103a7 field @Deprecated public static final int password = 16843100; // 0x101015c field public static final int path = 16842794; // 0x101002a - field public static final int pathAdvancedPattern = 16844319; // 0x101061f + field public static final int pathAdvancedPattern = 16844320; // 0x1010620 field public static final int pathData = 16843781; // 0x1010405 field public static final int pathPattern = 16842796; // 0x101002c field public static final int pathPrefix = 16842795; // 0x101002b - field public static final int pathSuffix = 16844317; // 0x101061d + field public static final int pathSuffix = 16844318; // 0x101061e field public static final int patternPathData = 16843978; // 0x10104ca field public static final int permission = 16842758; // 0x1010006 field public static final int permissionFlags = 16843719; // 0x10103c7 @@ -1152,7 +1152,7 @@ package android { field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603 - field public static final int requireDeviceScreenOn = 16844316; // 0x101061c + field public static final int requireDeviceScreenOn = 16844317; // 0x101061d field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 @@ -1293,10 +1293,10 @@ package android { field public static final int spotShadowAlpha = 16843967; // 0x10104bf field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 - field public static final int sspAdvancedPattern = 16844320; // 0x1010620 + field public static final int sspAdvancedPattern = 16844321; // 0x1010621 field public static final int sspPattern = 16843749; // 0x10103e5 field public static final int sspPrefix = 16843748; // 0x10103e4 - field public static final int sspSuffix = 16844318; // 0x101061e + field public static final int sspSuffix = 16844319; // 0x101061f field public static final int stackFromBottom = 16843005; // 0x10100fd field public static final int stackViewStyle = 16843838; // 0x101043e field public static final int starStyle = 16842882; // 0x1010082 @@ -10839,6 +10839,8 @@ package android.content { field public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; field public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; field public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT"; + field public static final String ACTION_PROFILE_ACCESSIBLE = "android.intent.action.PROFILE_ACCESSIBLE"; + field public static final String ACTION_PROFILE_INACCESSIBLE = "android.intent.action.PROFILE_INACCESSIBLE"; field public static final String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED"; field public static final String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK"; field public static final String ACTION_QUICK_VIEW = "android.intent.action.QUICK_VIEW"; @@ -12051,7 +12053,6 @@ package android.content.pm { public static class PackageInstaller.Session implements java.io.Closeable { method public void abandon(); - method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException; method public void addChildSessionId(int); method public void close(); method public void commit(@NonNull android.content.IntentSender); @@ -12065,6 +12066,7 @@ package android.content.pm { method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException; method public void removeChildSessionId(int); method public void removeSplit(@NonNull String) throws java.io.IOException; + method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException; method public void setStagingProgress(float); method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; } @@ -49396,8 +49398,9 @@ package android.view { method public void show(int); field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10 field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8 - field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1 - field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0 + field public static final int BEHAVIOR_DEFAULT = 1; // 0x1 + field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1 + field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0 field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2 } @@ -49458,7 +49461,6 @@ package android.view { field public static final int FLAGS_CHANGED = 4; // 0x4 field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1 field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000 - field @Deprecated public static final int FLAG_BLUR_BEHIND = 4; // 0x4 field public static final int FLAG_DIM_BEHIND = 2; // 0x2 field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000 field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8a6cdf5fe294..a7602f8ad288 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -137,6 +137,7 @@ package android { field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION"; + field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION"; field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; @@ -253,6 +254,7 @@ package android { field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES"; field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; + field public static final String USE_BACKGROUND_BLUR = "android.permission.USE_BACKGROUND_BLUR"; field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; @@ -284,6 +286,8 @@ package android { field public static final int sdkVersion = 16844304; // 0x1010610 field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int userRestriction = 16844164; // 0x1010584 + field public static final int windowBackgroundBlurEnabled = 16844316; // 0x101061c + field public static final int windowBackgroundBlurRadius = 16844315; // 0x101061b } public static final class R.bool { @@ -369,6 +373,8 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method public void setDeviceLocales(@NonNull android.os.LocaleList); method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean startProfile(@NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public boolean stopProfile(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); } @@ -1945,6 +1951,7 @@ package android.content { field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; + field public static final String UI_TRANSLATION_SERVICE = "ui_translation"; field public static final String VR_SERVICE = "vrmanager"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; @@ -2575,6 +2582,18 @@ package android.debug { } +package android.graphics.drawable { + + public final class BackgroundBlurDrawable extends android.graphics.drawable.Drawable { + ctor @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public BackgroundBlurDrawable(); + method public void setBlurRadius(int); + method public void setColor(@ColorInt int); + method public void setCornerRadius(float); + method public void setCornerRadius(float, float, float, float); + } + +} + package android.hardware { public final class Sensor { @@ -10229,6 +10248,7 @@ package android.telecom { method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean); method public final void resetConnectionTime(); method public void setCallDirection(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); @@ -10405,6 +10425,7 @@ package android.telecom { } public final class RemoteConnection { + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean); method @Deprecated public void setAudioState(android.telecom.AudioState); } @@ -13357,9 +13378,11 @@ package android.view { public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable { method public final long getUserActivityTimeout(); method public final void setUserActivityTimeout(long); + field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public static final int FLAG_BLUR_BEHIND = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000 field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY) public static final int SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY = 8; // 0x8 + field @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius; } @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SYSTEM_APPLICATION_OVERLAY}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags { @@ -13433,6 +13456,17 @@ package android.view.contentcapture { } +package android.view.translation { + + public final class UiTranslationManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, int); + } + +} + package android.webkit { public abstract class CookieManager { diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/core/java/android/accessibilityservice/OWNERS +++ b/core/java/android/accessibilityservice/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 8c62e9c4b2e8..520959cc40e7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3883,6 +3883,52 @@ public class ActivityManager { } /** + * Starts a profile. + * To be used with non-managed profiles, managed profiles should use + * {@link UserManager#requestQuietModeEnabled} + * + * @param userHandle user handle of the profile. + * @return true if the profile has been successfully started or if the profile is already + * running, false if profile failed to start. + * @throws IllegalArgumentException if {@code userHandle} is not a profile. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public boolean startProfile(@NonNull UserHandle userHandle) { + try { + return getService().startProfile(userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Stops a running profile. + * To be used with non-managed profiles, managed profiles should use + * {@link UserManager#requestQuietModeEnabled} + * + * @param userHandle user handle of the profile. + * @return true if the profile has been successfully stopped or is already stopped. Otherwise + * the exceptions listed below are thrown. + * @throws IllegalArgumentException if {@code userHandle} is not a profile. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public boolean stopProfile(@NonNull UserHandle userHandle) { + try { + return getService().stopProfile(userHandle.getIdentifier()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Updates the MCC (Mobile Country Code) and MNC (Mobile Network Code) in the * system configuration. * diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index f7f42a6e2713..986051cccd51 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -98,7 +98,7 @@ public abstract class ActivityManagerInternal { public abstract void killForegroundAppsForUser(@UserIdInt int userId); /** - * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions + * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions * such as Power Save mode. * @param target * @param whitelistToken @@ -132,9 +132,14 @@ public abstract class ActivityManagerInternal { /** * Update information about which app IDs are on the temp whitelist. - */ - public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, - boolean adding); + * @param appids the updated list of appIds in temp allowlist. + * @param changingUid uid to add or remove to temp allowlist. + * @param adding true to add to temp allowlist, false to remove from temp allowlist. + * @param durationMs when adding is true, the duration to be in temp allowlist. + * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}. + */ + public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, + boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type); /** * Get the procstate for the UID. The return value will be between diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a23dd35fa55f..161b7313b893 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1645,7 +1645,7 @@ public class AppOpsManager { */ private static int[] sOpToSwitch = new int[] { OP_COARSE_LOCATION, // COARSE_LOCATION - OP_COARSE_LOCATION, // FINE_LOCATION + OP_FINE_LOCATION, // FINE_LOCATION OP_COARSE_LOCATION, // GPS OP_VIBRATE, // VIBRATE OP_READ_CONTACTS, // READ_CONTACTS diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 9fdff5979cd0..f7097fab6b9e 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -470,9 +470,6 @@ public class ExitTransitionCoordinator extends ActivityTransitionCoordinator { || mSharedElementsHidden)) { finish(); } - if (!mIsReturning && mExitNotified) { - mExitCallbacks = null; // don't need it anymore - } } private void finish() { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 1b8f04902842..0019fd1908b3 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -686,4 +686,22 @@ interface IActivityManager { * {@link android.content.pm.PackageManager#getHoldLockToken()}. */ void holdLock(in IBinder token, in int durationMs); + + /** + * Starts a profile. + * @param userId the user id of the profile. + * @return true if the profile has been successfully started or if the profile is already + * running, false if profile failed to start. + * @throws IllegalArgumentException if the user is not a profile. + */ + boolean startProfile(int userId); + + /** + * Stops a profile. + * @param userId the user id of the profile. + * @return true if the profile has been successfully stopped or is already stopped. Otherwise + * the exceptions listed below are thrown. + * @throws IllegalArgumentException if the user is not a profile. + */ + boolean stopProfile(int userId); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index fe13fd48109a..bbda87132559 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -210,6 +210,7 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; import android.view.translation.ITranslationManager; import android.view.translation.TranslationManager; +import android.view.translation.UiTranslationManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -1187,6 +1188,19 @@ public final class SystemServiceRegistry { return null; }}); + registerService(Context.UI_TRANSLATION_SERVICE, UiTranslationManager.class, + new CachedServiceFetcher<UiTranslationManager>() { + @Override + public UiTranslationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE); + ITranslationManager service = ITranslationManager.Stub.asInterface(b); + if (service != null) { + return new UiTranslationManager(ctx.getOuterContext(), service); + } + return null; + }}); + registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class, new CachedServiceFetcher<SearchUiManager>() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index eb70ae11f38b..219014076c31 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4520,6 +4520,15 @@ public abstract class Context { public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; /** + * Official published name of the translation service which supports ui translation function. + * + * @hide + * @see #getSystemService(String) + */ + @SystemApi + public static final String UI_TRANSLATION_SERVICE = "ui_translation"; + + /** * Used for getting content selections and classifications for task snapshots. * * @hide diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 13a138102800..7843d97aa411 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3660,7 +3660,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent by the system when a user is started. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only sent to + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only sent to * registered receivers, not manifest receivers. It is sent to the user * that has been started. This is sent as a foreground * broadcast, since it is part of a visible user interaction; be as quick @@ -3672,7 +3672,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent when a user is in the process of starting. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only * sent to registered receivers, not manifest receivers. It is sent to all * users (including the one that is being started). You must hold * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive @@ -3689,7 +3689,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent when a user is going to be stopped. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is only + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only * sent to registered receivers, not manifest receivers. It is sent to all * users (including the one that is being stopped). You must hold * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive @@ -3707,7 +3707,7 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast sent to the system when a user is stopped. Carries an extra - * EXTRA_USER_HANDLE that has the userHandle of the user. This is similar to + * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is similar to * {@link #ACTION_PACKAGE_RESTARTED}, but for an entire user instead of a * specific package. This is only sent to registered receivers, not manifest * receivers. It is sent to all running users <em>except</em> the one that @@ -3811,6 +3811,22 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGED_PROFILE_UNAVAILABLE"; /** + * Broadcast sent to the parent user when an associated profile has been started and unlocked. + * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. + * This is only sent to registered receivers, not manifest receivers. + */ + public static final String ACTION_PROFILE_ACCESSIBLE = + "android.intent.action.PROFILE_ACCESSIBLE"; + + /** + * Broadcast sent to the parent user when an associated profile has stopped. + * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. + * This is only sent to registered receivers, not manifest receivers. + */ + public static final String ACTION_PROFILE_INACCESSIBLE = + "android.intent.action.PROFILE_INACCESSIBLE"; + + /** * Broadcast sent to the system user when the 'device locked' state changes for any user. * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which * the device was locked or unlocked. diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java new file mode 100644 index 000000000000..045c55f28bf1 --- /dev/null +++ b/core/java/android/content/pm/AppSearchPerson.java @@ -0,0 +1,159 @@ +/* + * 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Person; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.net.UriCodec; + +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.UUID; + +/** + * @hide + */ +public class AppSearchPerson extends GenericDocument { + + /** The name of the schema type for {@link Person} documents.*/ + public static final String SCHEMA_TYPE = "Person"; + + public static final String KEY_NAME = "name"; + public static final String KEY_KEY = "key"; + public static final String KEY_IS_BOT = "isBot"; + public static final String KEY_IS_IMPORTANT = "isImportant"; + + private AppSearchPerson(@NonNull GenericDocument document) { + super(document); + } + + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_KEY) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_BOT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_IMPORTANT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).build(); + + /** hide */ + @NonNull + public static AppSearchPerson instance(@NonNull final Person person) { + Objects.requireNonNull(person); + final String id; + if (person.getUri() != null) { + id = person.getUri(); + } else { + // NOTE: an identifier is required even when uri is null. + id = UUID.randomUUID().toString(); + } + return new Builder(id).setName(person.getName()) + .setKey(person.getKey()).setIsBot(person.isBot()) + .setIsImportant(person.isImportant()).build(); + } + + /** hide */ + @NonNull + public Person toPerson() { + String uri; + try { + uri = UriCodec.decode( + getUri(), false /* convertPlus */, StandardCharsets.UTF_8, + true /* throwOnFailure */); + } catch (IllegalArgumentException e) { + uri = null; + } + return new Person.Builder().setName(getPropertyString(KEY_NAME)) + .setUri(uri).setKey(getPropertyString(KEY_KEY)) + .setBot(getPropertyBoolean(KEY_IS_BOT)) + .setImportant(getPropertyBoolean(KEY_IS_IMPORTANT)).build(); + } + + /** @hide */ + @VisibleForTesting + public static class Builder extends GenericDocument.Builder<Builder> { + + public Builder(@NonNull final String id) { + super(id, SCHEMA_TYPE); + } + + /** @hide */ + @NonNull + public Builder setName(@Nullable final CharSequence name) { + if (name != null) { + setPropertyString(KEY_NAME, name.toString()); + } + return this; + } + + /** @hide */ + @NonNull + public Builder setKey(@Nullable final String key) { + if (key != null) { + setPropertyString(KEY_KEY, key); + } + return this; + } + + /** @hide */ + @NonNull + public Builder setIsBot(final boolean isBot) { + setPropertyBoolean(KEY_IS_BOT, isBot); + return this; + } + + /** @hide */ + @NonNull + public Builder setIsImportant(final boolean isImportant) { + setPropertyBoolean(KEY_IS_IMPORTANT, isImportant); + return this; + } + + @NonNull + @Override + public AppSearchPerson build() { + return new AppSearchPerson(super.build()); + } + } +} diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java new file mode 100644 index 000000000000..14b8df86025c --- /dev/null +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -0,0 +1,616 @@ +/* + * 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.Person; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.content.ComponentName; +import android.content.Intent; +import android.content.LocusId; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * @hide + */ +public class AppSearchShortcutInfo extends GenericDocument { + + /** The name of the schema type for {@link ShortcutInfo} documents.*/ + public static final String SCHEMA_TYPE = "Shortcut"; + + public static final String KEY_PACKAGE_NAME = "packageName"; + public static final String KEY_ACTIVITY = "activity"; + public static final String KEY_TITLE = "title"; + public static final String KEY_TEXT = "text"; + public static final String KEY_DISABLED_MESSAGE = "disabledMessage"; + public static final String KEY_CATEGORIES = "categories"; + public static final String KEY_INTENTS = "intents"; + public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras"; + public static final String KEY_PERSON = "person"; + public static final String KEY_LOCUS_ID = "locusId"; + public static final String KEY_RANK = "rank"; + public static final String KEY_EXTRAS = "extras"; + public static final String KEY_FLAGS = "flags"; + public static final String KEY_ICON_RES_ID = "iconResId"; + public static final String KEY_ICON_RES_NAME = "iconResName"; + public static final String KEY_ICON_URI = "iconUri"; + public static final String KEY_BITMAP_PATH = "bitmapPath"; + public static final String KEY_DISABLED_REASON = "disabledReason"; + + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PACKAGE_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ACTIVITY) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TITLE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TEXT) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_MESSAGE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CATEGORIES) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENTS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENT_PERSISTABLE_EXTRAS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PERSON) + .setSchemaType(AppSearchPerson.SCHEMA_TYPE) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_LOCUS_ID) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_RANK) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_EXTRAS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FLAGS) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_ID) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_NAME) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_URI) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BITMAP_PATH) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_REASON) + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .build() + + ).build(); + + public AppSearchShortcutInfo(@NonNull GenericDocument document) { + super(document); + } + + /** + * @hide + */ + @NonNull + public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) { + Objects.requireNonNull(shortcutInfo); + return new Builder(shortcutInfo.getId()) + .setActivity(shortcutInfo.getActivity()) + .setPackageName(shortcutInfo.getPackage()) + .setTitle(shortcutInfo.getShortLabel()) + .setText(shortcutInfo.getLongLabel()) + .setDisabledMessage(shortcutInfo.getDisabledMessage()) + .setCategories(shortcutInfo.getCategories()) + .setIntents(shortcutInfo.getIntents()) + .setRank(shortcutInfo.getRank()) + .setExtras(shortcutInfo.getExtras()) + .setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp()) + .setFlags(shortcutInfo.getFlags()) + .setIconResId(shortcutInfo.getIconResourceId()) + .setIconResName(shortcutInfo.getIconResName()) + .setBitmapPath(shortcutInfo.getBitmapPath()) + .setIconUri(shortcutInfo.getIconUri()) + .setDisabledReason(shortcutInfo.getDisabledReason()) + .setPersons(shortcutInfo.getPersons()) + .setLocusId(shortcutInfo.getLocusId()) + .build(); + } + + /** + * @hide + */ + @NonNull + public ShortcutInfo toShortcutInfo() { + return toShortcutInfo(UserHandle.myUserId()); + } + + /** + * @hide + * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides. + */ + @NonNull + public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) { + final String packageName = getPropertyString(KEY_PACKAGE_NAME); + final String activityString = getPropertyString(KEY_ACTIVITY); + final ComponentName activity = activityString == null + ? null : ComponentName.unflattenFromString(activityString); + // TODO: proper icon handling + // NOTE: bitmap based icons are currently saved in side-channel (see ShortcutBitmapSaver), + // re-creating Icon object at creation time implies turning this function into async since + // loading bitmap is I/O bound. Since ShortcutInfo#getIcon is already annotated with + // @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with + // LauncherApps#getShortcutIconDrawable instead. + final Icon icon = null; + final String title = getPropertyString(KEY_TITLE); + final String text = getPropertyString(KEY_TEXT); + final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE); + final String[] categories = getPropertyStringArray(KEY_CATEGORIES); + final Set<String> categoriesSet = categories == null + ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories)); + final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS); + final Intent[] intents = intentsStrings == null + ? null : Arrays.stream(intentsStrings).map(uri -> { + try { + return Intent.parseUri(uri, /* flags =*/ 0); + } catch (URISyntaxException e) { + // ignore malformed entry + } + return null; + }).toArray(Intent[]::new); + final byte[][] intentExtrasesBytes = getPropertyBytesArray(KEY_INTENT_PERSISTABLE_EXTRAS); + final Bundle[] intentExtrases = intentExtrasesBytes == null + ? null : Arrays.stream(intentExtrasesBytes) + .map(this::transformToBundle).toArray(Bundle[]::new); + if (intents != null) { + for (int i = 0; i < intents.length; i++) { + final Intent intent = intents[i]; + if (intent != null) { + intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]); + } + } + } + final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON)); + final String locusIdString = getPropertyString(KEY_LOCUS_ID); + final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString); + final int rank = (int) getPropertyLong(KEY_RANK); + final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS); + final PersistableBundle extras = transformToPersistableBundle(extrasByte); + final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS)); + final int iconResId = (int) getPropertyLong(KEY_ICON_RES_ID); + final String iconResName = getPropertyString(KEY_ICON_RES_NAME); + final String iconUri = getPropertyString(KEY_ICON_URI); + final String bitmapPath = getPropertyString(KEY_BITMAP_PATH); + final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON); + return new ShortcutInfo( + userId, getUri(), packageName, activity, icon, title, 0, null, + text, 0, null, disabledMessage, 0, null, + categoriesSet, intents, rank, extras, + getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, + disabledReason, persons, locusId); + } + + /** @hide */ + @VisibleForTesting + public static class Builder extends GenericDocument.Builder<Builder> { + + public Builder(String id) { + super(id, SCHEMA_TYPE); + } + + /** + * @hide + */ + @NonNull + public Builder setLocusId(@Nullable final LocusId locusId) { + if (locusId != null) { + setPropertyString(KEY_LOCUS_ID, locusId.getId()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setActivity(@Nullable final ComponentName activity) { + if (activity != null) { + setPropertyString(KEY_ACTIVITY, activity.flattenToShortString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setTitle(@Nullable final CharSequence shortLabel) { + if (!TextUtils.isEmpty(shortLabel)) { + setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty( + shortLabel, "shortLabel cannot be empty").toString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setText(@Nullable final CharSequence longLabel) { + if (!TextUtils.isEmpty(longLabel)) { + setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty( + longLabel, "longLabel cannot be empty").toString()); + } + return this; + + } + + /** + * @hide + */ + @NonNull + public Builder setDisabledMessage(@Nullable final CharSequence disabledMessage) { + if (!TextUtils.isEmpty(disabledMessage)) { + setPropertyString(KEY_DISABLED_MESSAGE, Preconditions.checkStringNotEmpty( + disabledMessage, "disabledMessage cannot be empty").toString()); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setCategories(@Nullable final Set<String> categories) { + if (categories != null && !categories.isEmpty()) { + setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new)); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setIntent(@Nullable final Intent intent) { + if (intent == null) { + return this; + } + return setIntents(new Intent[]{intent}); + } + + /** + * @hide + */ + @NonNull + public Builder setIntents(@Nullable final Intent[] intents) { + if (intents == null || intents.length == 0) { + return this; + } + for (Intent intent : intents) { + Objects.requireNonNull(intent, "intents cannot contain null"); + Objects.requireNonNull(intent.getAction(), "intent's action must be set"); + } + final byte[][] intentExtrases = new byte[intents.length][]; + for (int i = 0; i < intents.length; i++) { + final Intent intent = intents[i]; + final Bundle extras = intent.getExtras(); + intentExtrases[i] = extras == null + ? new byte[0] : transformToByteArray(new PersistableBundle(extras)); + } + + setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it -> + it.toUri(0)).toArray(String[]::new)); + setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setPerson(@Nullable final Person person) { + if (person == null) { + return this; + } + return setPersons(new Person[]{person}); + } + + /** + * @hide + */ + @NonNull + public Builder setPersons(@Nullable final Person[] persons) { + if (persons == null || persons.length == 0) { + return this; + } + setPropertyDocument(KEY_PERSON, + Arrays.stream(persons).map(person -> AppSearchPerson.instance( + Objects.requireNonNull(person, "persons cannot contain null")) + ).toArray(AppSearchPerson[]::new)); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setRank(final int rank) { + Preconditions.checkArgument((0 <= rank), + "Rank cannot be negative or bigger than MAX_RANK"); + setPropertyLong(KEY_RANK, rank); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setExtras(@Nullable final PersistableBundle extras) { + if (extras != null) { + setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras)); + } + return this; + } + + /** + * @hide + */ + public Builder setPackageName(@Nullable final String packageName) { + if (!TextUtils.isEmpty(packageName)) { + setPropertyString(KEY_PACKAGE_NAME, packageName); + } + return this; + } + + /** + * @hide + */ + public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) { + setPropertyLong(KEY_FLAGS, flattenFlags(flags)); + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setIconResId(@Nullable final int iconResId) { + setPropertyLong(KEY_ICON_RES_ID, iconResId); + return this; + } + + /** + * @hide + */ + public Builder setIconResName(@Nullable final String iconResName) { + if (!TextUtils.isEmpty(iconResName)) { + setPropertyString(KEY_ICON_RES_NAME, iconResName); + } + return this; + } + + /** + * @hide + */ + public Builder setBitmapPath(@Nullable final String bitmapPath) { + if (!TextUtils.isEmpty(bitmapPath)) { + setPropertyString(KEY_BITMAP_PATH, bitmapPath); + } + return this; + } + + /** + * @hide + */ + public Builder setIconUri(@Nullable final String iconUri) { + if (!TextUtils.isEmpty(iconUri)) { + setPropertyString(KEY_ICON_URI, iconUri); + } + return this; + } + + /** + * @hide + */ + public Builder setDisabledReason(@ShortcutInfo.DisabledReason final int disabledReason) { + setPropertyLong(KEY_DISABLED_REASON, disabledReason); + return this; + } + + /** + * @hide + */ + @NonNull + @Override + public AppSearchShortcutInfo build() { + return new AppSearchShortcutInfo(super.build()); + } + } + + /** + * Convert PersistableBundle into byte[] for persistence. + */ + @Nullable + private static byte[] transformToByteArray(@NonNull final PersistableBundle extras) { + Objects.requireNonNull(extras); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + new PersistableBundle(extras).writeToStream(baos); + return baos.toByteArray(); + } catch (IOException e) { + return null; + } + } + + /** + * Convert byte[] into Bundle. + */ + @Nullable + private Bundle transformToBundle(@Nullable final byte[] extras) { + if (extras == null) { + return null; + } + Objects.requireNonNull(extras); + try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) { + final Bundle ret = new Bundle(); + ret.putAll(PersistableBundle.readFromStream(bais)); + return ret; + } catch (IOException e) { + return null; + } + } + + /** + * Convert byte[] into PersistableBundle. + */ + @Nullable + private PersistableBundle transformToPersistableBundle(@Nullable final byte[] extras) { + if (extras == null) { + return null; + } + try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) { + return PersistableBundle.readFromStream(bais); + } catch (IOException e) { + return null; + } + } + + private static long[] flattenFlags(@ShortcutInfo.ShortcutFlags final int flags) { + final List<Integer> flattenedFlags = new ArrayList<>(); + flattenedFlags.add(0); + for (int i = 0; i < 31; i++) { + final int mask = 1 << i; + if ((flags & mask) != 0) { + flattenedFlags.add(mask); + } + } + return flattenedFlags.stream().mapToLong(i -> i).toArray(); + } + + private static int parseFlags(final long[] flags) { + return (int) Arrays.stream(flags).reduce((p, v) -> p | v).getAsLong(); + } + + @NonNull + private static Person[] parsePerson(@Nullable final GenericDocument[] persons) { + return persons == null ? new Person[0] : Arrays.stream(persons).map(it -> + ((AppSearchPerson) it).toPerson()).toArray(Person[]::new); + } +} diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 9a73be9d44a4..f72288c670d9 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -35,7 +35,7 @@ interface IPackageInstallerSession { void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); void stageViaHardLink(String target); - void addChecksums(String name, in Checksum[] checksums); + void setChecksums(String name, in Checksum[] checksums, in byte[] signature); void removeSplit(String splitName); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 01d4c2801b24..248be0f09510 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1247,12 +1247,15 @@ public class PackageInstaller { } /** - * Adds installer-provided checksums for the APK file in session. + * Sets installer-provided checksums for the APK file in session. * * @param name previously written as part of this session. * {@link #openWrite} * @param checksums installer intends to make available via * {@link PackageManager#requestChecksums}. + * @param signature PKCS#7 detached signature bytes over serialized checksums to enable + * fs-verity for the checksums or null if fs-verity should not be enabled. + * @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a> * @throws SecurityException if called after the session has been * committed or abandoned. * @throws IllegalStateException if checksums for this file have already been added. @@ -1262,13 +1265,14 @@ public class PackageInstaller { * in {@link PackageManager#requestChecksums}. */ @Deprecated - public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums) - throws IOException { + public void setChecksums(@NonNull String name, @NonNull List<Checksum> checksums, + @Nullable byte[] signature) throws IOException { Objects.requireNonNull(name); Objects.requireNonNull(checksums); try { - mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()])); + mSession.setChecksums(name, checksums.toArray(new Checksum[checksums.size()]), + signature); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index da75fba18f82..522f4ca88519 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -2191,7 +2191,7 @@ public final class ShortcutInfo implements Parcelable { dest.writeString8(mIconUri); } - public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR = + public static final @NonNull Creator<ShortcutInfo> CREATOR = new Creator<ShortcutInfo>() { public ShortcutInfo createFromParcel(Parcel source) { return new ShortcutInfo(source); diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 04b585cdf420..80ac64b87d4d 100644 --- a/core/java/android/net/vcn/IVcnManagementService.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -16,6 +16,7 @@ package android.net.vcn; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.os.ParcelUuid; @@ -25,4 +26,7 @@ import android.os.ParcelUuid; interface IVcnManagementService { void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName); void clearVcnConfig(in ParcelUuid subscriptionGroup); + + void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); + void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); } diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl new file mode 100644 index 000000000000..f8ae492016f0 --- /dev/null +++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl @@ -0,0 +1,22 @@ +/* + * 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 android.net.vcn; + +/** @hide */ +interface IVcnUnderlyingNetworkPolicyListener { + void onPolicyChanged(); +}
\ No newline at end of file diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index b881a339535b..2ccdc2633af0 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -25,7 +25,12 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; +import com.android.internal.annotations.VisibleForTesting; + import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; /** * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks. @@ -60,6 +65,11 @@ import java.io.IOException; public final class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + @VisibleForTesting + public static final Map< + VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); + @NonNull private final Context mContext; @NonNull private final IVcnManagementService mService; @@ -136,4 +146,101 @@ public final class VcnManager { throw e.rethrowFromSystemServer(); } } + + // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi + /** + * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components + * can register to receive updates for VCN-underlying Network policies from the System Server. + * + * @hide + */ + public interface VcnUnderlyingNetworkPolicyListener { + /** + * Notifies the implementation that the VCN's underlying Network policy has changed. + * + * <p>After receiving this callback, implementations MUST poll VcnManager for the updated + * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy. + */ + void onPolicyChanged(); + } + + /** + * Add a listener for VCN-underlying network policy updates. + * + * @param executor the Executor that will be used for invoking all calls to the specified + * Listener + * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is + * already registered + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(executor, "executor must not be null"); + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); + if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { + throw new IllegalArgumentException( + "Attempting to add a listener that is already in use"); + } + + try { + mService.addVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + REGISTERED_POLICY_LISTENERS.remove(listener); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * + * <p>If the specified listener is not currently registered, this is a no-op. + * + * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @hide + */ + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + REGISTERED_POLICY_LISTENERS.remove(listener); + if (binder == null) { + return; + } + + try { + mService.removeVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System + * Server. + * + * @hide + */ + private static class VcnUnderlyingNetworkPolicyListenerBinder + extends IVcnUnderlyingNetworkPolicyListener.Stub { + @NonNull private final Executor mExecutor; + @NonNull private final VcnUnderlyingNetworkPolicyListener mListener; + + private VcnUnderlyingNetworkPolicyListenerBinder( + Executor executor, VcnUnderlyingNetworkPolicyListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onPolicyChanged() { + mExecutor.execute(() -> mListener.onPolicyChanged()); + } + } } diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl new file mode 100644 index 000000000000..6cb6ee685a64 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl @@ -0,0 +1,20 @@ +/* + * 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 android.net.vcn; + +/** @hide */ +parcelable VcnUnderlyingNetworkPolicy; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java new file mode 100644 index 000000000000..dd7c86d87ff2 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -0,0 +1,110 @@ +/* + * 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 android.net.vcn; + +import android.annotation.NonNull; +import android.net.NetworkCapabilities; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * VcnUnderlyingNetworkPolicy represents the Network policy for a VCN-managed Network. + * + * <p>Transports that are bringing up networks capable of acting as a VCN's underlying network + * should query for policy state upon major capability changes (e.g. changing of TRUSTED bit), and + * when prompted by VcnManagementService via VcnUnderlyingNetworkPolicyListener. + * + * @hide + */ +public final class VcnUnderlyingNetworkPolicy implements Parcelable { + private final boolean mIsTearDownRequested; + private final NetworkCapabilities mMergedNetworkCapabilities; + + /** + * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters. + * + * @hide + */ + public VcnUnderlyingNetworkPolicy( + boolean isTearDownRequested, @NonNull NetworkCapabilities mergedNetworkCapabilities) { + Objects.requireNonNull( + mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull"); + + mIsTearDownRequested = isTearDownRequested; + mMergedNetworkCapabilities = mergedNetworkCapabilities; + } + + /** + * Returns whether this Carrier VCN policy policy indicates that the underlying Network should + * be torn down. + */ + public boolean isTeardownRequested() { + return mIsTearDownRequested; + } + + /** + * Returns the NetworkCapabilities with Carrier VCN policy bits merged into the provided + * capabilities. + */ + @NonNull + public NetworkCapabilities getMergedNetworkCapabilities() { + return mMergedNetworkCapabilities; + } + + @Override + public int hashCode() { + return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false; + final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o; + + return mIsTearDownRequested == that.mIsTearDownRequested + && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mIsTearDownRequested); + dest.writeParcelable(mMergedNetworkCapabilities, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = + new Creator<VcnUnderlyingNetworkPolicy>() { + public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { + return new VcnUnderlyingNetworkPolicy( + in.readBoolean(), in.readParcelable(null)); + } + + public VcnUnderlyingNetworkPolicy[] newArray(int size) { + return new VcnUnderlyingNetworkPolicy[size]; + } + }; +} diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 59292baa110c..9fdc72bbe6c6 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -36,6 +36,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFileParcel; import android.text.TextUtils; @@ -70,7 +71,8 @@ public final class IncrementalFileStorages { @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { + @NonNull PerUidReadTimeouts[] perUidReadTimeouts, + IPackageLoadingProgressCallback progressCallback) throws IOException { // TODO(b/136132412): validity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); @@ -95,7 +97,11 @@ public final class IncrementalFileStorages { throw new IOException("Unknown file location: " + file.location); } } - + // Register progress loading callback after files have been added + if (progressCallback != null) { + incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(), + progressCallback); + } result.startLoading(); return result; @@ -180,6 +186,7 @@ public final class IncrementalFileStorages { try { mDefaultStorage.unBind(mStageDir.getAbsolutePath()); + mDefaultStorage.unregisterLoadingProgressListener(); } catch (IOException ignored) { } mDefaultStorage = null; diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 1878d61c78ac..8492363eb503 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -19,171 +19,268 @@ package android.text; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; -import android.net.Uri; import android.os.Build; +import android.os.LocaleList; +import java.io.File; import java.lang.annotation.Retention; +import java.util.List; /** * Font configuration descriptions for System fonts. - * @hide + * @hide // TODO Make this SystemApi. */ public final class FontConfig { - private final @NonNull Family[] mFamilies; - private final @NonNull Alias[] mAliases; + private final @NonNull List<Family> mFamilies; + private final @NonNull List<Alias> mAliases; - public FontConfig(@NonNull Family[] families, @NonNull Alias[] aliases) { + /** + * Construct a SystemFontConfig instance. + * + * @param families a list of font families. + * @param aliases a list of aliases. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public FontConfig(@NonNull List<Family> families, @NonNull List<Alias> aliases) { mFamilies = families; mAliases = aliases; } /** * Returns the ordered list of families included in the system fonts. + * + * @return a list of font families. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @NonNull Family[] getFamilies() { + public @NonNull List<Family> getFontFamilies() { return mFamilies; } /** * Returns the list of aliases defined for the font families in the system fonts. + * + * @return a list of font families. */ - public @NonNull Alias[] getAliases() { + public @NonNull List<Alias> getAliases() { return mAliases; } /** - * Class that holds information about a Font. + * Returns the ordered list of families included in the system fonts. + * @deprecated Use getFontFamilies instead. + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @NonNull Family[] getFamilies() { + return mFamilies.toArray(new Family[0]); + } + + /** + * A class represents single font entry in system font configuration. */ public static final class Font { - private final @NonNull String mFontName; - private final int mTtcIndex; - private final @NonNull FontVariationAxis[] mAxes; - private final int mWeight; - private final boolean mIsItalic; - private Uri mUri; - private final String mFallbackFor; + private final @NonNull File mFilePath; + private final @Nullable File mOriginalPath; + private final @NonNull FontStyle mStyle; + private final @IntRange(from = 0) int mIndex; + private final @NonNull String mFontVariationSettings; + private final @Nullable String mFallback; /** - * @hide + * Construct a Font instance. + * + * @hide Only system server can create this instance and passed via IPC. */ - public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes, - int weight, boolean isItalic, String fallbackFor) { - mFontName = fontName; - mTtcIndex = ttcIndex; - mAxes = axes; - mWeight = weight; - mIsItalic = isItalic; - mFallbackFor = fallbackFor; + public Font(@NonNull File filePath, @Nullable File originalPath, @NonNull FontStyle style, + @IntRange(from = 0) int index, @NonNull String fontVariationSettings, + @Nullable String fallback) { + mFilePath = filePath; + mOriginalPath = originalPath; + mStyle = style; + mIndex = index; + mFontVariationSettings = fontVariationSettings; + mFallback = fallback; + } + + /** + * Returns a file to the font file. + * + * @return a font file. + */ + public @NonNull File getFilePath() { + return mFilePath; + } + + /** + * Returns an original font file in the system directory. + * + * If the font file is not updated, returns null. + * + * @return returns the original font file in the system if the font file is updated. Returns + * null if the font file is not updated. + */ + public @Nullable File getOriginalPath() { + return mOriginalPath; + } + + /** + * Returns a font style. + * + * @return a font style. + */ + public @NonNull FontStyle getStyle() { + return mStyle; + } + + /** + * Returns a font index. + * + * @return a font index. + */ + public @IntRange(from = 0) int getIndex() { + return mIndex; } /** - * Returns the name associated by the system to this font. + * Return a font variation settings. + * + * @return a font variation settings. */ - public @NonNull String getFontName() { - return mFontName; + public @NonNull String getFontVariationSettings() { + return mFontVariationSettings; + } + + /** + * Returns font family name that uses this font as a fallback. + * + * If this font is a fallback for the default font family, this is null. + * + * @return a font family name. + */ + public @Nullable String getFallback() { + return mFallback; } /** * Returns the index to be used to access this font when accessing a TTC file. + * @deprecated Use getIndex instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getTtcIndex() { - return mTtcIndex; + return mIndex; } /** * Returns the list of axes associated to this font. + * @deprecated Use getFontVariationSettings + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @NonNull FontVariationAxis[] getAxes() { - return mAxes; + return FontVariationAxis.fromFontVariationSettings(mFontVariationSettings); } /** * Returns the weight value for this font. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public int getWeight() { - return mWeight; + return getStyle().getWeight(); } /** * Returns whether this font is italic. + * @deprecated Use getStyle instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isItalic() { - return mIsItalic; - } - - /** - * Returns the content uri associated to this font. - * - * You can reach to the font contents by calling {@link - * android.content.ContentResolver#openInputStream}. - */ - public @Nullable Uri getUri() { - return mUri; - } - - public void setUri(@NonNull Uri uri) { - mUri = uri; - } - - public String getFallbackFor() { - return mFallbackFor; + return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC; } } /** - * Class that holds information about a Font alias. + * A class represents alias between named font families. + * + * In the system font configuration, an font family can be an alias of another font family with + * different font weight. For example, "sans-serif-medium" can be a medium weight of + * sans-serif font family. */ public static final class Alias { - private final @NonNull String mName; - private final @NonNull String mToName; - private final int mWeight; + private final @NonNull String mAliasName; + private final @NonNull String mReferName; + private final @IntRange(from = 0, to = 1000) int mWeight; - public Alias(@NonNull String name, @NonNull String toName, int weight) { - mName = name; - mToName = toName; + /** + * Construct an alias instance. + * + * @param aliasName an alias of the named font family. + * @param referName a referring font family name. + * @param weight a font weight of the referring font family. + * @hide Only system server can create this instance and passed via IPC. + */ + public Alias(@NonNull String aliasName, @NonNull String referName, + @IntRange(from = 0, to = 1000) int weight) { + mAliasName = aliasName; + mReferName = referName; mWeight = weight; } /** - * Returns the new name for the alias. + * An alias of the named font family. + * + * @return an alias of the named font family. */ - public @NonNull String getName() { - return mName; + public @NonNull String getAliasName() { + return mAliasName; } /** - * Returns the existing name to which this alias points to. + * A name of font family referring from {@link #getAliasName()} + * + * @return a referring font family name. */ - public @NonNull String getToName() { - return mToName; + public @NonNull String getReferName() { + return mReferName; } /** - * Returns the weight associated with this alias. + * A font weight of the referring font family. + * + * @return a font weight of the referring font family. */ - public int getWeight() { + public @IntRange(from = 0, to = 1000) int getWeight() { return mWeight; } } /** - * Class that holds information about a Font family. + * A class represents single font family entry in system font configuration. + * + * <p> + * A font family is a bundle of fonts for drawing text in various styles. + * For example, regular style font and bold style font can be bundled into a single font family, + * then system will select the correct style font from family for drawing. */ public static final class Family { - private final @NonNull String mName; - private final @NonNull Font[] mFonts; - // Comma separated BCP47 complient locale strings - private final @NonNull String mLanguages; + private final @NonNull List<Font> mFonts; + private final @Nullable String mName; + private final @Nullable LocaleList mLocaleList; + private final @Variant int mVariant; /** @hide */ @Retention(SOURCE) @@ -212,52 +309,105 @@ public final class FontConfig { /** * Value for font variant. * - * Indiates the font is for elegant variant. + * Indicates the font is for elegant variant. * @see android.graphics.Paint#setElegantTextHeight */ public static final int VARIANT_ELEGANT = 2; - // Must be same with Minikin's variant values. - // See frameworks/minikin/include/minikin/FontFamily.h - private final @Variant int mVariant; - - public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String languages, - @Variant int variant) { - mName = name; + /** + * Construct a family instance. + * + * @hide Only system server can create this instance and passed via IPC. + */ + public Family(@NonNull List<Font> fonts, @Nullable String name, + @Nullable LocaleList localeList, @Variant int variant) { mFonts = fonts; - mLanguages = languages; + mName = name; + mLocaleList = localeList; mVariant = variant; } /** - * Returns the name given by the system to this font family. + * Returns a list of font files in this family. + * + * @return a list of font files. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable String getName() { + public @NonNull List<Font> getFontList() { + return mFonts; + } + + /** + * Returns a family name if this family defines a new fallback. + * + * @return non-null if a family name is associated. Otherwise null. + */ + public @Nullable String getFallbackName() { return mName; } /** - * Returns the list of fonts included in this family. + * Returns a locale list if associated. + * + * @return non-null if a locale list is associated. Otherwise null. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public @Nullable Font[] getFonts() { - return mFonts; + public @NonNull LocaleList getLocaleList() { + return mLocaleList; } /** - * Returns the comma separated BCP47 complient languages for this family. May be null. + * Returns a text height variant. + * + * @return text height variant. */ - public @NonNull String getLanguages() { - return mLanguages; + public @Variant int getTextHeightVariant() { + return mVariant; } /** - * Returns the font variant for this family, e.g. "elegant" or "compact". May be null. + * Returns a family variant associated. + * + * @return a family variant. + * @deprecated Use getTextHeightVariant instead. + * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public @Variant int getVariant() { return mVariant; } + + /** + * Returns a family name if associated. + * + * @return non-null if a family name is associated. Otherwise null. + * @deprecated Use getFallbackName instead. + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable String getName() { + return mName; + } + + /** + * Returns the list of fonts included in this family. + * @deprecated Use getFontFiles instead + * @hide + */ + @Deprecated + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + public @Nullable Font[] getFonts() { + return mFonts.toArray(new Font[0]); + } + + /** + * Returns the comma separated BCP47 compliant languages for this family. May be null. + * @deprecated Use getLocaleList instead + * @hide + */ + @Deprecated + public @NonNull String getLanguages() { + return mLocaleList.toLanguageTags(); + } } } diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java index 5a64a5d5b3a6..a334907c04bc 100644 --- a/core/java/android/view/InsetsFlags.java +++ b/core/java/android/view/InsetsFlags.java @@ -21,7 +21,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import android.view.WindowInsetsController.Appearance; @@ -60,13 +60,13 @@ public class InsetsFlags { @ViewDebug.ExportedProperty(flagMapping = { @ViewDebug.FlagToString( - mask = BEHAVIOR_SHOW_BARS_BY_SWIPE, - equals = BEHAVIOR_SHOW_BARS_BY_SWIPE, - name = "SHOW_BARS_BY_SWIPE"), + mask = BEHAVIOR_DEFAULT, + equals = BEHAVIOR_DEFAULT, + name = "DEFAULT"), @ViewDebug.FlagToString( mask = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, equals = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, name = "SHOW_TRANSIENT_BARS_BY_SWIPE") }) - public @Behavior int behavior; + public @Behavior int behavior = BEHAVIOR_DEFAULT; } diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index c018d1cf1782..c61baf6fb40c 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -100,6 +100,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mReplayedInsetsController != null) { return mReplayedInsetsController.getSystemBarsBehavior(); } + if (mBehavior == KEEP_BEHAVIOR) { + return BEHAVIOR_DEFAULT; + } return mBehavior; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 25967b3ebcff..04fe8978788b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3790,7 +3790,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only * has an effect when used in combination with that flag.</p> * - * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_BARS_BY_SWIPE} instead. + * @deprecated Use {@link WindowInsetsController#BEHAVIOR_DEFAULT} instead. */ @Deprecated public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800; @@ -9802,6 +9802,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private void notifyAttachForDrawables() { + if (mBackground != null) mBackground.onAttached(this); + if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) { + mForegroundInfo.mDrawable.onAttached(this); + } + } + + private void notifyDetachForDrawables() { + if (mBackground != null) mBackground.onDetached(this); + if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) { + mForegroundInfo.mDrawable.onDetached(this); + } + } + private void setNotifiedContentCaptureAppeared() { mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED; @@ -20653,6 +20667,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyEnterOrExitForAutoFillIfNeeded(true); notifyAppearedOrDisappearedForContentCaptureIfNeeded(true); + notifyAttachForDrawables(); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -20702,6 +20717,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, notifyEnterOrExitForAutoFillIfNeeded(false); notifyAppearedOrDisappearedForContentCaptureIfNeeded(false); + notifyDetachForDrawables(); } /** @@ -23830,6 +23846,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackground != null) { if (isAttachedToWindow()) { mBackground.setVisible(false, false); + mBackground.onDetached(this); } mBackground.setCallback(null); unscheduleDrawable(mBackground); @@ -23879,6 +23896,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, background.setState(getDrawableState()); } if (isAttachedToWindow()) { + background.onAttached(this); background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); } @@ -24111,6 +24129,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mForegroundInfo.mDrawable != null) { if (isAttachedToWindow()) { mForegroundInfo.mDrawable.setVisible(false, false); + mForegroundInfo.mDrawable.onDetached(this); } mForegroundInfo.mDrawable.setCallback(null); unscheduleDrawable(mForegroundInfo.mDrawable); @@ -24128,6 +24147,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } applyForegroundTint(); if (isAttachedToWindow()) { + foreground.onAttached(this); foreground.setVisible(getWindowVisibility() == VISIBLE && isShown(), false); } // Set callback last, since the view may still be initializing. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7e0ebbcc61d2..d6949623f377 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -26,7 +26,6 @@ import static android.view.InsetsState.SIZE; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; -import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE; import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; @@ -55,8 +54,7 @@ import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; @@ -117,6 +115,7 @@ import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; +import android.graphics.drawable.BackgroundBlurDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.display.DisplayManager; @@ -188,7 +187,6 @@ import android.window.ClientWindowFrames; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.drawable.BackgroundBlurDrawable; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; @@ -672,6 +670,14 @@ public final class ViewRootImpl implements ViewParent, new BackgroundBlurDrawable.Aggregator(this); /** + * @return {@link BackgroundBlurDrawable.Aggregator} for this instance. + */ + @NonNull + public BackgroundBlurDrawable.Aggregator getBlurRegionAggregator() { + return mBlurRegionAggregator; + } + + /** * @return {@link ImeFocusController} for this instance. */ @NonNull @@ -2167,10 +2173,8 @@ public final class ViewRootImpl implements ViewParent, if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0 || (flags & FLAG_FULLSCREEN) != 0) { inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - } else if ((sysUiVis & SYSTEM_UI_FLAG_IMMERSIVE) != 0) { - inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; } else { - inOutParams.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH; + inOutParams.insetsFlags.behavior = BEHAVIOR_DEFAULT; } } @@ -9154,14 +9158,14 @@ public final class ViewRootImpl implements ViewParent, * Handles an inbound request for scroll capture from the system. If a client is not already * active, a search will be dispatched through the view tree to locate scrolling content. * <p> - * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect, + * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect, * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned * depending on the results of the search. * * @param callbacks to receive responses * @see ScrollCaptureTargetResolver */ - private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { + public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); // Window (root) level callbacks @@ -9169,10 +9173,12 @@ public final class ViewRootImpl implements ViewParent, // Search through View-tree View rootView = getView(); - Point point = new Point(); - Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); - getChildVisibleRect(rootView, rect, point); - rootView.dispatchScrollCaptureSearch(rect, point, targetList); + if (rootView != null) { + Point point = new Point(); + Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); + getChildVisibleRect(rootView, rect, point); + rootView.dispatchScrollCaptureSearch(rect, point, targetList); + } // No-op path. Scroll capture not offered for this window. if (targetList.isEmpty()) { @@ -10048,13 +10054,6 @@ public final class ViewRootImpl implements ViewParent, } } - /** - * Creates a background blur drawable for the backing {@link Surface}. - */ - public BackgroundBlurDrawable createBackgroundBlurDrawable() { - return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext); - } - @Override public void onDescendantUnbufferedRequested() { mUnbufferedInputSource = mView.mUnbufferedInputSource; diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index e879bb4bff95..fb9bcbd2fcb2 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Insets; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.CancellationSignal; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsets.Type; @@ -77,22 +78,41 @@ public interface WindowInsetsController { } /** - * The default option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly - * shown on any user interaction on the corresponding display if navigation bars are hidden by + * Option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly shown on any + * user interaction on the corresponding display if navigation bars are hidden by * {@link #hide(int)} or * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. + * @deprecated This is not supported on Android {@link Build.VERSION_CODES#S} and later. Use + * {@link #BEHAVIOR_DEFAULT} or {@link #BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE} + * instead. */ + @Deprecated int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; /** + * The default option for {@link #setSystemBarsBehavior(int)}: Window would like to remain + * interactive when hiding navigation bars by calling {@link #hide(int)} or + * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. + * + * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such + * as swiping from the edge of the screen where the bar is hidden from.</p> + * + * <p>When the gesture navigation is enabled, the system gestures can be triggered regardless + * the visibility of system bars.</p> + */ + int BEHAVIOR_DEFAULT = 1; + + /** * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when * hiding navigation bars by calling {@link #hide(int)} or * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}. * * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such * as swiping from the edge of the screen where the bar is hidden from.</p> + * @deprecated Use {@link #BEHAVIOR_DEFAULT} instead. */ - int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; + @Deprecated + int BEHAVIOR_SHOW_BARS_BY_SWIPE = BEHAVIOR_DEFAULT; /** * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when @@ -111,8 +131,7 @@ public interface WindowInsetsController { * @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {BEHAVIOR_SHOW_BARS_BY_TOUCH, BEHAVIOR_SHOW_BARS_BY_SWIPE, - BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}) + @IntDef(value = {BEHAVIOR_DEFAULT, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE}) @interface Behavior { } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 45fa41b252aa..c2d990a40fe4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1508,9 +1508,13 @@ public interface WindowManager extends ViewManager { * Use {@link #dimAmount} to control the amount of dim. */ public static final int FLAG_DIM_BEHIND = 0x00000002; - /** Window flag: blur everything behind this window. - * @deprecated Blurring is no longer supported. */ - @Deprecated + /** Window flag: enable blurring behind this window. + * To set the amount of blur, use {@link #backgroundBlurRadius} + * + * @hide + */ + @RequiresPermission(permission.USE_BACKGROUND_BLUR) + @SystemApi public static final int FLAG_BLUR_BEHIND = 0x00000004; /** Window flag: this window won't ever get key input focus, so the @@ -3225,10 +3229,14 @@ public interface WindowManager extends ViewManager { public boolean preferMinimalPostProcessing = false; /** - * Indicates that this window wants to have blurred content behind it. + * When {@link FLAG_BLUR_BEHIND} is set, this is the amount of blur in pixels that this + * window will use to blur behind itself. + * The range is from 0, which means no blur, to 150. * * @hide */ + @SystemApi + @RequiresPermission(permission.USE_BACKGROUND_BLUR) public int backgroundBlurRadius = 0; /** diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS index 93b5a2e8bc28..b1d3967a8b04 100644 --- a/core/java/android/view/accessibility/OWNERS +++ b/core/java/android/view/accessibility/OWNERS @@ -9,3 +9,4 @@ sumir@google.com ogunwale@google.com jjaggi@google.com pweaver@google.com +ryanlwlin@google.com diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index 73addf4d1894..e1754531d761 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -16,10 +16,15 @@ package android.view.translation; +import android.content.ComponentName; +import android.os.IBinder; import android.service.translation.TranslationRequest; +import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; +import java.util.List; + /** * Mediator between apps being translated and translation service implementation. * @@ -29,4 +34,8 @@ oneway interface ITranslationManager { void getSupportedLocales(in IResultReceiver receiver, int userId); void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec, int sessionId, in IResultReceiver receiver, int userId); + + void updateUiTranslationState(int state, in TranslationSpec sourceSpec, + in TranslationSpec destSpec, in List<AutofillId> viewIds, in int taskId, + int userId); } diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java new file mode 100644 index 000000000000..eeb463ae0ed3 --- /dev/null +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -0,0 +1,179 @@ +/* + * 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 android.view.translation; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.content.Context; +import android.os.RemoteException; +import android.view.View; +import android.view.autofill.AutofillId; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Objects; + +/** + * The {@link UiTranslationManager} class provides ways for apps to use the ui translation + * function in framework. + * + * @hide + */ +@SystemApi +public final class UiTranslationManager { + + private static final String TAG = "UiTranslationManager"; + + /** + * The state caller request to disable utranslation,, it is no longer need to ui translation. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_STARTED = 0; + /** + * The state caller request to pause ui translation, it will switch back to the original text. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_PAUSED = 1; + /** + * The state caller request to resume the paused ui translation, it will show the translated + * text again if the text had been translated. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_RESUMED = 2; + /** + * The state the caller request to enable ui translation. + * + * @hide + */ + public static final int STATE_UI_TRANSLATION_FINISHED = 3; + /** + * @hide + */ + @IntDef(prefix = {"STATE__TRANSLATION"}, value = { + STATE_UI_TRANSLATION_STARTED, + STATE_UI_TRANSLATION_PAUSED, + STATE_UI_TRANSLATION_RESUMED, + STATE_UI_TRANSLATION_FINISHED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UiTranslationState { + } + + @NonNull + private final Context mContext; + + private final ITranslationManager mService; + + /** + * @hide + */ + public UiTranslationManager(@NonNull Context context, ITranslationManager service) { + mContext = Objects.requireNonNull(context); + mService = service; + } + + /** + * Request ui translation for a given Views. + * + * @param sourceSpec {@link TranslationSpec} for the data to be translated. + * @param destSpec {@link TranslationSpec} for the translated data. + * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void startTranslation(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds, + int taskId) { + // TODO(b/177789967): Return result code or find a way to notify the status. + // TODO(b/177394471): The is a temparary API, the expected is requestUiTranslation( + // TranslationSpec, TranslationSpec,List<AutofillId>, Binder). We may need more time to + // implement it, use task id as initial version for demo. + Objects.requireNonNull(sourceSpec); + Objects.requireNonNull(destSpec); + Objects.requireNonNull(viewIds); + + try { + mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec, + destSpec, viewIds, taskId, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to disable the ui translation. It will destroy all the {@link Translator}s and no + * longer to show to show the translated text. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void finishTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is finishUiTranslation( + // Binder). We may need more time to implement it, use task id as initial version. + mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to pause the current ui translation's {@link Translator} which will switch back to + * the original language. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void pauseTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is pauseUiTranslation(Binder) + // We may need more time to implement it, use task id as initial version for demo + mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to resume the paused ui translation's {@link Translator} which will switch to the + * translated language if the text had been translated. + * + * @param taskId the Activity Task id which needs ui translation + */ + @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) + public void resumeTranslation(int taskId) { + try { + // TODO(b/177394471): The is a temparary API, the expected is resumeUiTranslation( + // Binder). We may need more time to implement it, use task id as initial version. + mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED, + null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, + taskId, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index b9ff26b7d9ff..6281ee9d05d1 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -39,6 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -776,17 +777,23 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Grows {@code r} from its center such that each dimension is at least {@code minimumSize}. + * + * The result will still have the same {@link Rect#centerX()} and {@link Rect#centerY()} as the + * input. + * + * @hide */ - private void growRectTo(Rect r, int minimumSize) { - int dy = (minimumSize - r.height()) / 2; + @VisibleForTesting + public void growRectTo(Rect r, int minimumSize) { + int dy = minimumSize - r.height(); if (dy > 0) { - r.top -= dy; - r.bottom += dy; + r.top -= (dy + 1) / 2; + r.bottom += dy / 2; } - int dx = (minimumSize - r.width()) / 2; + int dx = minimumSize - r.width(); if (dx > 0) { - r.left -= dx; - r.right += dx; + r.left -= (dx + 1) / 2; + r.right += dx / 2; } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c235c82de720..6cfd49888fac 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -182,7 +182,7 @@ public class ChooserActivity extends ResolverActivity implements * To be used for shared element transition into this activity. * @hide */ - public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "chooser_preview_image_1"; + public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "screenshot_preview_image"; private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions"; diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java index c0cc483648fa..47d83346d038 100644 --- a/core/java/com/android/internal/app/ChooserActivityLogger.java +++ b/core/java/com/android/internal/app/ChooserActivityLogger.java @@ -120,7 +120,7 @@ public interface ChooserActivityLogger { @UiEvent(doc = "User selected the nearby target.") SHARESHEET_NEARBY_TARGET_SELECTED(626), @UiEvent(doc = "User selected the edit target.") - SHARESHEET_EDIT_TARGET_SELECTED(627); + SHARESHEET_EDIT_TARGET_SELECTED(669); private final int mId; SharesheetTargetSelectedEvent(int id) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 33aa19078c9f..f105320d0353 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -19,6 +19,8 @@ package com.android.internal.os; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; +import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -103,7 +105,6 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; -import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.power.MeasuredEnergyStats.EnergyBucket; import com.android.internal.util.ArrayUtils; @@ -370,14 +371,6 @@ public class BatteryStatsImpl extends BatteryStats { * @param railStats */ void fillRailDataStats(RailStats railStats); - /** - * Function to get energy consumption data - * - * @return an array of measured energy (in microjoules) since boot, will be null if - * measured energy data is unavailable - */ - @Nullable - MeasuredEnergyArray getEnergyConsumptionData(); } public static abstract class UserInfoProvider { @@ -10704,15 +10697,13 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - MeasuredEnergyRetriever energyStatsCb, boolean[] supportedEnergyBuckets, - UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, supportedEnergyBuckets, - userInfoProvider); + MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb, - boolean[] supportedEnergyBuckets, UserInfoProvider userInfoProvider) { + UserInfoProvider userInfoProvider) { init(clocks); if (systemDir == null) { @@ -10818,10 +10809,6 @@ public class BatteryStatsImpl extends BatteryStats { // Notify statsd that the system is initially not in doze. mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); - - mGlobalMeasuredEnergyStats = supportedEnergyBuckets == null ? null : - new MeasuredEnergyStats(supportedEnergyBuckets); - mScreenStateAtLastEnergyMeasurement = mScreenState; } @UnsupportedAppUsage @@ -12503,21 +12490,6 @@ public class BatteryStatsImpl extends BatteryStats { } /** - * Get energy consumed (in microjoules) by a set of subsystems from the {@link - * MeasuredEnergyRetriever}, if available. - * - * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is - * unavailable. - */ - @Nullable - public MeasuredEnergyArray getEnergyConsumptionDataLocked() { - if (mMeasuredEnergyRetriever == null) { - return null; - } - return mMeasuredEnergyRetriever.getEnergyConsumptionData(); - } - - /** * Read and distribute kernel wake lock use across apps. */ public void updateKernelWakelocksLocked() { @@ -14240,6 +14212,40 @@ public class BatteryStatsImpl extends BatteryStats { mConstants.startObserving(context.getContentResolver()); registerUsbStateReceiver(context); } + /** + * Initialize the measured energy stats data structures. + * + * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported + */ + @GuardedBy("this") + public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) { + boolean supportedBucketMismatch = false; + mScreenStateAtLastEnergyMeasurement = mScreenState; + + if (supportedEnergyBuckets == null) { + if (mGlobalMeasuredEnergyStats != null) { + // Measured energy buckets no longer supported, wipe out the existing data. + supportedBucketMismatch = true; + } + } else if (mGlobalMeasuredEnergyStats == null) { + mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets); + return; + } else { + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i) + != supportedEnergyBuckets[i]) { + supportedBucketMismatch = true; + break; + } + } + } + + if (supportedBucketMismatch) { + // Supported energy buckets changed since last boot. + // Existing data is no longer reliable. + resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); + } + } @VisibleForTesting public final class Constants extends ContentObserver { @@ -14919,7 +14925,11 @@ public class BatteryStatsImpl extends BatteryStats { mNextMaxDailyDeadlineMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); - MeasuredEnergyStats.readSummaryFromParcel(mGlobalMeasuredEnergyStats, in); + /** + * WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled + * later when {@link #initMeasuredEnergyStatsLocked} is called. + */ + mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in); mStartCount++; @@ -15417,7 +15427,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mNextMaxDailyDeadlineMs); out.writeLong(mBatteryTimeToFullSeconds); - MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out); + MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false); mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); @@ -15742,7 +15752,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } - MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out); + MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out, true); final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); int NW = wakeStats.size(); diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 790d7f7ab694..6860759eea8a 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -69,15 +69,16 @@ public class WrapperInit { // Tell the Zygote what our actual PID is (since it only knows about the // wrapper that it directly forked). if (fdNum != 0) { + FileDescriptor fd = new FileDescriptor(); try { - FileDescriptor fd = new FileDescriptor(); fd.setInt$(fdNum); DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); os.writeInt(Process.myPid()); os.close(); - IoUtils.closeQuietly(fd); } catch (IOException ex) { Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } finally { + IoUtils.closeQuietly(fd); } } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 141dc79f4c93..3be841cb2431 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2540,8 +2540,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - params.backgroundBlurRadius = a.getDimensionPixelSize( - R.styleable.Window_windowBackgroundBlurRadius, 0); + if (a.getBoolean(R.styleable.Window_windowBackgroundBlurEnabled, false)) { + if ((getForcedWindowFlags() & WindowManager.LayoutParams.FLAG_BLUR_BEHIND) == 0) { + params.flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND; + } + + params.backgroundBlurRadius = a.getDimensionPixelSize( + android.R.styleable.Window_windowBackgroundBlurRadius, 0); + } if (params.windowAnimations == 0) { params.windowAnimations = a.getResourceId( diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index b644df46c7e3..b744a5d57e98 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -130,12 +130,16 @@ public class MeasuredEnergyStats { * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary * parceling changes. */ - private void readSummaryFromParcel(Parcel in) { + private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) { final int size = in.readInt(); for (int i = 0; i < size; i++) { final int bucket = in.readInt(); final long energyUJ = in.readLong(); - setValueIfSupported(bucket, energyUJ); + if (overwriteAvailability) { + mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; + } else { + setValueIfSupported(bucket, energyUJ); + } } } @@ -143,14 +147,15 @@ public class MeasuredEnergyStats { * Write to summary parcel. * Note: Measured subsystem availability may be different when the summary parcel is read. */ - private void writeSummaryToParcel(Parcel out) { + private void writeSummaryToParcel(Parcel out, boolean skipZero) { final int sizePos = out.dataPosition(); out.writeInt(0); int size = 0; // Write only the supported buckets with non-zero energy. for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { final long energy = mAccumulatedEnergiesMicroJoules[i]; - if (energy <= 0) continue; + if (energy < 0) continue; + if (energy == 0 && skipZero) continue; out.writeInt(i); out.writeLong(mAccumulatedEnergiesMicroJoules[i]); @@ -197,16 +202,19 @@ public class MeasuredEnergyStats { } /** - * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and - * ignore the parcelled data. + * Create a MeasuredEnergyStats object from a summary parcel. + * + * @return a new MeasuredEnergyStats object as described. + * Returns null if the parcel indicates there is no data to populate. */ - public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) { + public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) { // Check if any MeasuredEnergyStats exists on the parcel - if (in.readInt() == 0) return; + if (in.readInt() == 0) return null; - // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data - final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats(); - mes.readSummaryFromParcel(in); + final MeasuredEnergyStats stats = + new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]); + stats.readSummaryFromParcel(in, true); + return stats; } /** @@ -227,12 +235,12 @@ public class MeasuredEnergyStats { if (template == null) { // Nothing supported now. Create placeholder object just to consume the parcel data. final MeasuredEnergyStats mes = new MeasuredEnergyStats(); - mes.readSummaryFromParcel(in); + mes.readSummaryFromParcel(in, false); return null; } final MeasuredEnergyStats stats = createFromTemplate(template); - stats.readSummaryFromParcel(in); + stats.readSummaryFromParcel(in, false); if (stats.containsInterestingData()) { return stats; } else { @@ -253,13 +261,13 @@ public class MeasuredEnergyStats { * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. */ public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, - Parcel dest) { + Parcel dest, boolean skipZero) { if (stats == null) { dest.writeInt(0); return; } dest.writeInt(1); - stats.writeSummaryToParcel(dest); + stats.writeSummaryToParcel(dest, skipZero); } /** Reset accumulated energy. */ diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index b9c2fd532d0f..200e0dd6e65b 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -41,7 +41,6 @@ oneway interface IStatusBar void showWirelessChargingAnimation(int batteryLevel); - void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive); void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition, boolean showImeSwitcher, boolean isMultiClientImeEnabled); void setWindowState(int display, int window, int state); @@ -167,7 +166,7 @@ oneway interface IStatusBar void onRecentsAnimationStateChanged(boolean running); /** - * Notifies System UI side of system bar appearance change on the specified display. + * Notifies System UI side of system bar attribute change on the specified display. * * @param displayId the ID of the display to notify * @param appearance the appearance of the focused window. The light top bar appearance is not @@ -177,9 +176,12 @@ oneway interface IStatusBar * bar, that the bar can have partial appearances in corresponding * stacks. * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. + * @param behavior the behavior of the focused window. + * @param isFullscreen whether any of status or navigation bar is requested invisible. */ - void onSystemBarAppearanceChanged(int displayId, int appearance, - in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme); + void onSystemBarAttributesChanged(int displayId, int appearance, + in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + int behavior, boolean isFullscreen); /** * Notifies System UI to show transient bars. The transient bars are system bars, e.g., status diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index 9095f05543da..8fb2f9cd8bf9 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -38,14 +38,14 @@ public final class RegisterStatusBarResult implements Parcelable { public final int mDisabledFlags2; // switch[6] public final IBinder mImeToken; public final boolean mNavbarColorManagedByIme; + public final int mBehavior; public final boolean mAppFullscreen; - public final boolean mAppImmersive; public final int[] mTransientBarTypes; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis, int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken, - boolean navbarColorManagedByIme, boolean appFullscreen, boolean appImmersive, + boolean navbarColorManagedByIme, int behavior, boolean appFullscreen, @NonNull int[] transientBarTypes) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; @@ -57,8 +57,8 @@ public final class RegisterStatusBarResult implements Parcelable { mDisabledFlags2 = disabledFlags2; mImeToken = imeToken; mNavbarColorManagedByIme = navbarColorManagedByIme; + mBehavior = behavior; mAppFullscreen = appFullscreen; - mAppImmersive = appImmersive; mTransientBarTypes = transientBarTypes; } @@ -79,8 +79,8 @@ public final class RegisterStatusBarResult implements Parcelable { dest.writeInt(mDisabledFlags2); dest.writeStrongBinder(mImeToken); dest.writeBoolean(mNavbarColorManagedByIme); + dest.writeInt(mBehavior); dest.writeBoolean(mAppFullscreen); - dest.writeBoolean(mAppImmersive); dest.writeIntArray(mTransientBarTypes); } @@ -103,13 +103,13 @@ public final class RegisterStatusBarResult implements Parcelable { final int disabledFlags2 = source.readInt(); final IBinder imeToken = source.readStrongBinder(); final boolean navbarColorManagedByIme = source.readBoolean(); + final int behavior = source.readInt(); final boolean appFullscreen = source.readBoolean(); - final boolean appImmersive = source.readBoolean(); final int[] transientBarTypes = source.createIntArray(); return new RegisterStatusBarResult(icons, disabledFlags1, appearance, appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher, - disabledFlags2, imeToken, navbarColorManagedByIme, appFullscreen, - appImmersive, transientBarTypes); + disabledFlags2, imeToken, navbarColorManagedByIme, behavior, + appFullscreen, transientBarTypes); } @Override diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index 0fb29111d043..a9db91be1d5b 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -257,7 +257,17 @@ jobject JHwBlob::NewObject(JNIEnv *env, size_t size) { // XXX Again cannot refer to gFields.constructID because InitClass may // not have been called yet. - return env->NewObject(clazz.get(), constructID, size); + // Cases: + // - this originates from another process (something so large should not fit + // in the binder buffer, and it should be rejected by the binder driver) + // - if this is used in process, this code makes too many heap copies (in + // order to retrofit HIDL's scatter-gather format to java types) to + // justify passing such a large amount of data over this path. So the + // alternative (updating the constructor and other code to accept other + // types, should also probably not be taken in this case). + CHECK_LE(size, std::numeric_limits<jint>::max()); + + return env->NewObject(clazz.get(), constructID, static_cast<jint>(size)); } } // namespace android diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index fa046c6593af..d3c2d31779db 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -626,6 +626,7 @@ message ActivityManagerServiceDumpProcessesProto { optional int32 target_uid = 1; optional int64 duration_ms = 2; optional string tag = 3; + optional int32 type = 4; } repeated PendingTempWhitelist pending_temp_whitelist = 26; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d5750bc183d0..c9579ed8fc22 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -617,6 +617,9 @@ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" /> <protected-broadcast android:name="com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK" /> + <protected-broadcast android:name="android.intent.action.PROFILE_ACCESSIBLE" /> + <protected-broadcast android:name="android.intent.action.PROFILE_INACCESSIBLE" /> + <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" /> <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" /> @@ -3617,6 +3620,15 @@ <permission android:name="android.permission.BIND_TRANSLATION_SERVICE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows apps to use ui translation functions. + <p>Protection level: signature|privileged + <p> TODO(b/177703453): Restrict to a specific Role instead of allowing access to any + privileged app. + @hide Not for use by third-party applications. + --> + <permission android:name="android.permission.MANAGE_UI_TRANSLATION" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). @@ -4075,6 +4087,11 @@ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to use background blur. + @hide --> + <permission android:name="android.permission.USE_BACKGROUND_BLUR" + android:protectionLevel="signature|privileged" /> + <!-- Allows an application to control the lights on the device. @hide @SystemApi diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index be7ecfc00f01..9bfd5f5e10f8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -87,6 +87,16 @@ theme does not set this value, meaning it is based on whether the window is floating. --> <attr name="backgroundDimEnabled" format="boolean" /> + <!-- When windowBackgroundBlurEnabled is set, this is the amount of blur to apply + behind the window. The range is from 0, which means no blur, to 150. + @hide @SystemApi --> + <attr name="windowBackgroundBlurRadius" format="dimension"/> + <!-- If set, the area behind the window will be blurred with radius + windowBackgroundBlurRadius. + @hide @SystemApi --> + <attr name="windowBackgroundBlurEnabled" format="boolean" /> + + <!-- Color of background imagery used for popup windows. --> <attr name="colorPopupBackground" format="color" /> <!-- Color used for list divider. --> @@ -1964,6 +1974,8 @@ <attr name="textColor" /> <attr name="backgroundDimEnabled" /> <attr name="backgroundDimAmount" /> + <attr name="windowBackgroundBlurEnabled" /> + <attr name="windowBackgroundBlurRadius" /> <attr name="windowActionBar" /> <attr name="windowActionModeOverlay" /> <attr name="windowActionBarOverlay" /> @@ -2181,10 +2193,6 @@ the decor view. --> <attr name="windowLightNavigationBar" format="boolean" /> - <!-- @hide --> - <attr name="windowBackgroundBlurRadius" format="dimension"/> - - <!-- Controls how the window is laid out if there is a {@code DisplayCutout}. <p> Defaults to {@code default}. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 0185714dc342..bb2665584600 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2486,7 +2486,8 @@ <attr name="singleUser" /> <attr name="directBootAware" /> <attr name="visibleToInstantApps" /> - <!-- The code for this component is located in the given split. --> + <!-- The code for this component is located in the given split. + @deprecated Do not use it. This is not supported. --> <attr name="splitName" /> </declare-styleable> @@ -2602,7 +2603,8 @@ must also be {@link android.R.attr#exported} if this flag is set. --> <attr name="externalService" format="boolean" /> <attr name="visibleToInstantApps" /> - <!-- The code for this component is located in the given split. --> + <!-- The code for this component is located in the given split. + @deprecated Do not use it. This is not supported. --> <attr name="splitName" /> <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service will be spawned from an Application Zygote, instead of the regular Zygote. diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index bac50f1b2c07..ddf3c5f09e9e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3049,7 +3049,10 @@ <public name="windowLayoutAffinity" /> <public name="canPauseRecording" /> <!-- @hide --> + <!-- @hide @SystemApi --> <public name="windowBackgroundBlurRadius"/> + <!-- @hide @SystemApi --> + <public name="windowBackgroundBlurEnabled"/> <public name="requireDeviceScreenOn" /> <public name="pathSuffix" /> <public name="sspSuffix" /> diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java new file mode 100644 index 000000000000..1ff88f70019e --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java @@ -0,0 +1,47 @@ +/* + * 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 android.content.pm; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Person; + +import org.junit.Test; + +public class AppSearchPersonTest { + + @Test + public void testBuildPersonAndGetValue() { + final String name = "name"; + final String key = "key"; + final String uri = "name:name"; + + final Person person = new AppSearchPerson.Builder(uri) + .setName(name) + .setKey(key) + .setIsBot(true) + .setIsImportant(false) + .build() + .toPerson(); + + assertThat(person.getName()).isEqualTo(name); + assertThat(person.getKey()).isEqualTo(key); + assertThat(person.getUri()).isEqualTo(uri); + assertThat(person.isBot()).isTrue(); + assertThat(person.isImportant()).isFalse(); + } +} diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java new file mode 100644 index 000000000000..da92e69b6378 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java @@ -0,0 +1,69 @@ +/* + * 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 android.content.pm; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Person; +import android.content.ComponentName; +import android.content.Intent; +import android.util.ArraySet; + +import org.junit.Test; + +import java.util.Set; + +public class AppSearchShortcutInfoTest { + + @Test + public void testBuildShortcutAndGetValue() { + final String category = + "android.app.stubs.SHARE_SHORTCUT_CATEGORY"; + final String id = "shareShortcut"; + final String shortcutIconResName = "shortcut"; + final ComponentName activity = new ComponentName("xxx", "s"); + final Person person = new Person.Builder() + .setBot(false) + .setName("BubbleBot") + .setImportant(true) + .build(); + + final Set<String> categorySet = new ArraySet<>(); + categorySet.add(category); + final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); + final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id) + .setActivity(activity) + .setText(id) + .setIconResName(shortcutIconResName) + .setIntent(shortcutIntent) + .setPerson(person) + .setCategories(categorySet) + .setFlags(ShortcutInfo.FLAG_LONG_LIVED) + .build() + .toShortcutInfo(); + + assertThat(shortcut.getId()).isEqualTo(id); + assertThat(shortcut.getShortLabel()).isEqualTo(id); + assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName); + assertThat(shortcut.getIntent().toString()).isEqualTo(shortcut.toString()); + assertThat(shortcut.getPersons().length).isEqualTo(1); + assertThat(shortcut.getPersons()[0]).isEqualTo(person); + assertThat(shortcut.getCategories()).isEqualTo(categorySet); + assertThat(shortcut.getFlags()).isEqualTo(ShortcutInfo.FLAG_LONG_LIVED); + assertThat(shortcut.getActivity()).isEqualTo(activity); + } +} diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 465ea172b1c0..65ea2a8373aa 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -21,6 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.FontCustomizationParser; @@ -44,7 +46,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; @@ -139,27 +140,55 @@ public class TypefaceSystemFallbackTest { } } - private static void buildSystemFallback(String xml, - FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, - ArrayMap<String, FontFamily[]> fallbackMap) { + private static void buildSystemFallback( + @NonNull String xml, + @Nullable String oemXml, + @NonNull ArrayMap<String, Typeface> outFontMap, + @NonNull ArrayMap<String, FontFamily[]> outFallbackMap) { try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { - fos.write(xml.getBytes(Charset.forName("UTF-8"))); + fos.write(xml.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new RuntimeException(e); } + + String oemXmlPath; + if (oemXml != null) { + try (FileOutputStream fos = new FileOutputStream(TEST_OEM_XML)) { + fos.write(oemXml.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + oemXmlPath = TEST_OEM_XML; + } else { + oemXmlPath = null; + } + Map<String, File> updatableFontMap = new HashMap<>(); for (File file : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { updatableFontMap.put(file.getName(), file); } - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, updatableFontMap, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + + outFontMap.clear(); + outFontMap.putAll(typefaceMap); + outFallbackMap.clear(); + outFallbackMap.putAll(fallbackMap); } private static FontCustomizationParser.Result readFontCustomization(String oemXml) { try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) { - return FontCustomizationParser.parse(is, TEST_OEM_DIR); + return FontCustomizationParser.parse(is, TEST_OEM_DIR, null); } catch (IOException | XmlPullParserException e) { throw new RuntimeException(e); } @@ -167,19 +196,22 @@ public class TypefaceSystemFallbackTest { @Test public void testBuildSystemFallback() { - final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, oemCustomization, fallbackMap); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse( + SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + assertFalse(fontConfig.getAliases().isEmpty()); + assertFalse(fontConfig.getFontFamilies().isEmpty()); - assertNotNull(aliases); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); assertFalse(fallbackMap.isEmpty()); - Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); - assertFalse(fontMap.isEmpty()); + Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces( + fontConfig, fallbackMap); + assertFalse(typefaceMap.isEmpty()); } @Test @@ -199,10 +231,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); assertEquals(1, fontMap.size()); assertTrue(fontMap.containsKey("sans-serif")); @@ -229,10 +259,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -277,10 +305,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -324,10 +350,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -376,10 +400,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -424,10 +446,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -465,10 +485,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -506,10 +524,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -556,10 +572,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); paint.setTypeface(fontMap.get("sans-serif")); @@ -600,10 +614,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -641,10 +653,8 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -679,10 +689,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -717,10 +725,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -751,10 +757,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -804,10 +808,8 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - readFontCustomization(oemXml); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, oemXml, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -862,12 +864,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); // Install all2em.ttf as a3em.ttf copyAssetToFile("fonts/all2em.ttf", new File(TEST_UPDATABLE_FONT_DIR, "a3em.ttf")); - buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + buildSystemFallback(xml, null, fontMap, fallbackMap); final Paint paint = new Paint(); diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 392c6b7199a5..d12f495055e1 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -27,7 +27,6 @@ import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.text.FontConfig; -import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -41,7 +40,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.nio.ByteOrder; -import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -197,10 +195,10 @@ public class TypefaceTest { @SmallTest @Test public void testSerialize() throws Exception { - HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res = - SystemFonts.initializePreinstalledFonts(); - Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first); + FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig, + fallbackMap); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 64e6f82d82af..e301037d01f1 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -19,14 +19,15 @@ package android.text; import android.annotation.NonNull; import android.content.Context; import android.content.res.AssetManager; +import android.graphics.FontListParser; import android.graphics.Typeface; -import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; -import android.util.ArrayMap; import androidx.test.InstrumentationRegistry; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -34,12 +35,13 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.util.Map; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; private final String mXml; private final String mTestFontsDir; - final ArrayMap<String, Typeface> mFontMap = new ArrayMap<>(); + private final Map<String, Typeface> mFontMap; public FontFallbackSetup(@NonNull String testSubDir, @NonNull String[] testFontFiles, @NonNull String xml) { @@ -75,12 +77,15 @@ public class FontFallbackSetup implements AutoCloseable { throw new RuntimeException(e); } - final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final FontCustomizationParser.Result oemCustomization = - new FontCustomizationParser.Result(); - final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, oemCustomization, fallbackMap); - Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); + FontConfig fontConfig; + try { + fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + + Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig); + mFontMap = SystemFonts.buildSystemTypefaces(fontConfig, fallbackMap); } @NonNull diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index f371a7ffce23..c67174f0ae1e 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -23,7 +23,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -35,6 +35,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; import android.os.Binder; @@ -50,6 +51,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + /** * Tests for {@link ViewRootImpl} * @@ -180,7 +184,7 @@ public class ViewRootImplTest { public void adjustLayoutParamsForCompatibility_noAdjustBehavior() { final WindowInsetsController controller = mViewRootImpl.getInsetsController(); final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes; - final int behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; + final int behavior = BEHAVIOR_DEFAULT; controller.setSystemBarsBehavior(behavior); attrs.systemUiVisibility = SYSTEM_UI_FLAG_IMMERSIVE_STICKY; ViewRootImpl.adjustLayoutParamsForCompatibility(attrs); @@ -196,6 +200,26 @@ public class ViewRootImplTest { } /** + * Ensure scroll capture request handles a ViewRootImpl with no view tree. + */ + @Test + public void requestScrollCapture_withoutContentRoot() { + final CountDownLatch latch = new CountDownLatch(1); + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + @Override + public void onUnavailable() { + latch.countDown(); + } + }); + try { + if (latch.await(100, TimeUnit.MILLISECONDS)) { + return; // pass + } + } catch (InterruptedException e) { /* ignore */ } + fail("requestScrollCapture did not respond"); + } + + /** * When window doesn't have focus, keys should be dropped. */ @Test diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java index 5371a0f8d9d7..ccd873dc390e 100644 --- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java +++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; import android.platform.test.annotations.Presubmit; -import android.view.View; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -48,6 +47,7 @@ import java.util.List; @Presubmit public class AbsSeekBarTest { + public static final int PADDING = 10; private Context mContext; private AbsSeekBar mBar; @@ -59,34 +59,42 @@ public class AbsSeekBarTest { @Test public void testExclusionForThumb_limitedTo48dp() { - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); + + final int thumbOffset = mBar.getThumbOffset(); + measureAndLayout(dpToPxSize(200), dpToPxSize(100)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height()); assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width()); } @Test public void testExclusionForThumb_limitedToHeight() { - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); + + final int thumbOffset = mBar.getThumbOffset(); + measureAndLayout(dpToPxSize(200), dpToPxSize(32)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height()); assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width()); } @@ -95,7 +103,7 @@ public class AbsSeekBarTest { public void testExclusionForThumb_passesThroughUserExclusions() { mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4))); - mBar.setPadding(10, 10, 10, 10); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); @@ -110,12 +118,37 @@ public class AbsSeekBarTest { assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2)); } + @Test + public void testGrowRectTo_evenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 0, 0), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 1, 1), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference_unevenSize() { + doGrowRectTest(new Rect(0, 0, 0, 0), 9, new Rect(-5, -5, 4, 4)); + } + + public void doGrowRectTest(Rect in, int minimumSize, Rect expected) { + Rect result = new Rect(in); + mBar.growRectTo(result, minimumSize); + + assertEquals("grown rect", expected, result); + assertEquals("grown rect center point", center(expected), center(result)); + } + private Point center(Rect rect) { return new Point(rect.centerX(), rect.centerY()); } - private Point center(View view) { - return center(new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + private Rect offset(Rect rect, int dx, int dy) { + Rect result = new Rect(rect); + result.offset(dx, dy); + return result; } private ShapeDrawable newThumb(int size) { diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index 2ede7517c306..c9c81ac51944 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -34,6 +34,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** * Test class for {@link MeasuredEnergyStats}. * @@ -114,7 +116,7 @@ public class MeasuredEnergyStatsTest { } @Test - public void testReadWriteSummaryParcel() { + public void testCreateAndReadSummaryFromParcel() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; @@ -126,35 +128,21 @@ public class MeasuredEnergyStatsTest { stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); final Parcel parcel = Parcel.obtain(); - MeasuredEnergyStats.writeSummaryToParcel(stats, parcel); - - - final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true - newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false - MeasuredEnergyStats newStats = new MeasuredEnergyStats(newSupportedEnergyBuckets); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); parcel.setDataPosition(0); - MeasuredEnergyStats.readSummaryFromParcel(newStats, parcel); + MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel); for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { - if (!newSupportedEnergyBuckets[i]) { - assertFalse(newStats.isEnergyBucketSupported(i)); - assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); - } else if (!supportedEnergyBuckets[i]) { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); - } else { - assertTrue(newStats.isEnergyBucketSupported(i)); - assertEquals(stats.getAccumulatedBucketEnergy(i), - newStats.getAccumulatedBucketEnergy(i)); - } + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); } parcel.recycle(); } @Test - public void testCreateAndReadSummaryFromParcel() { + public void testCreateAndReadSummaryFromParcel_existingTemplate() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false; @@ -171,7 +159,7 @@ public class MeasuredEnergyStatsTest { stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); final Parcel parcel = Parcel.obtain(); - MeasuredEnergyStats.writeSummaryToParcel(stats, parcel); + MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; @@ -200,6 +188,55 @@ public class MeasuredEnergyStatsTest { } @Test + public void testCreateAndReadSummaryFromParcel_skipZero() { + final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; + Arrays.fill(supportedEnergyBuckets, true); + + final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets); + // Accumulate energy in one bucket, the rest should be zero + stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); + + final Parcel includeZerosParcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false); + includeZerosParcel.setDataPosition(0); + + MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel( + includeZerosParcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (i == ENERGY_BUCKET_SCREEN_ON) { + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); + } else { + assertTrue(newStats.isEnergyBucketSupported(i)); + assertEquals(0L, newStats.getAccumulatedBucketEnergy(i)); + } + } + includeZerosParcel.recycle(); + + final Parcel skipZerosParcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true); + skipZerosParcel.setDataPosition(0); + + newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + if (i == ENERGY_BUCKET_SCREEN_ON) { + assertEquals(stats.isEnergyBucketSupported(i), + newStats.isEnergyBucketSupported(i)); + assertEquals(stats.getAccumulatedBucketEnergy(i), + newStats.getAccumulatedBucketEnergy(i)); + } else { + assertFalse(newStats.isEnergyBucketSupported(i)); + assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i)); + } + } + skipZerosParcel.recycle(); + } + + @Test public void testUpdateBucket() { final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS]; supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true; diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java index 7eca320d4aeb..272f2287dd6e 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java @@ -16,6 +16,8 @@ package com.android.internal.statusbar; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + import static com.google.common.truth.Truth.assertThat; import android.os.Binder; @@ -56,8 +58,8 @@ public class RegisterStatusBarResultTest { 0x20 /* disabledFlags2 */, new Binder() /* imeToken */, true /* navbarColorManagedByIme */, + BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, true /* appFullscreen */, - true /* appImmersive */, new int[0] /* transientBarTypes */); final RegisterStatusBarResult copy = clone(original); @@ -76,8 +78,8 @@ public class RegisterStatusBarResultTest { assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2); assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken); assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme); + assertThat(copy.mBehavior).isEqualTo(original.mBehavior); assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen); - assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive); assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes); } diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml index 32666c8d9f68..6132d53b4651 100644 --- a/data/etc/car/com.android.car.shell.xml +++ b/data/etc/car/com.android.car.shell.xml @@ -15,7 +15,9 @@ ~ limitations under the License --> <permissions> - <privapp-permissions package="com.android.car.shell"> + <!-- CarShell now overrides the shell package and adding permission here + is ok. --> + <privapp-permissions package="com.android.shell"> <permission name="android.permission.INSTALL_PACKAGES" /> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> </privapp-permissions> diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index af100c96f6f5..ff381176e38d 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,10 +16,14 @@ package android.graphics; +import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.graphics.fonts.FontCustomizationParser; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.FontVariationAxis; import android.os.Build; +import android.os.LocaleList; import android.text.FontConfig; import android.util.Xml; @@ -27,6 +31,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -36,7 +41,6 @@ import java.util.regex.Pattern; /** * Parser for font config files. - * * @hide */ public class FontListParser { @@ -44,49 +48,89 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { - return parse(in, "/system/fonts", null); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null); } /** - * Parse the fonts.xml + * Parses system font config XMLs + * + * @param fontsXmlPath location of fonts.xml + * @param systemFontDir location of system font directory + * @param oemCustomizationXmlPath location of oem_customization.xml + * @param productFontDir location of oem customized font directory + * @param updatableFontMap map of updated font files. + * @return font configuration + * @throws IOException + * @throws XmlPullParserException */ - public static FontConfig parse(InputStream in, String fontDir, - @Nullable Map<String, File> updatableFontMap) - throws XmlPullParserException, IOException { - try { + public static FontConfig parse( + @NonNull String fontsXmlPath, + @NonNull String systemFontDir, + @Nullable String oemCustomizationXmlPath, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) throws IOException, XmlPullParserException { + FontCustomizationParser.Result oemCustomization; + if (oemCustomizationXmlPath != null) { + try (InputStream is = new FileInputStream(oemCustomizationXmlPath)) { + oemCustomization = FontCustomizationParser.parse(is, productFontDir, + updatableFontMap); + } catch (IOException e) { + // OEM customization may not exists. Ignoring + oemCustomization = new FontCustomizationParser.Result(); + } + } else { + oemCustomization = new FontCustomizationParser.Result(); + } + + try (InputStream is = new FileInputStream(fontsXmlPath)) { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + parser.setInput(is, null); parser.nextTag(); - return readFamilies(parser, fontDir, updatableFontMap); - } finally { - in.close(); + return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap); } } - private static FontConfig readFamilies(XmlPullParser parser, String fontDir, + private static FontConfig readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull FontCustomizationParser.Result customization, @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); - List<FontConfig.Alias> aliases = new ArrayList<>(); + List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases()); + + Map<String, FontConfig.Family> oemNamedFamilies = + customization.getAdditionalNamedFamilies(); parser.require(XmlPullParser.START_TAG, null, "familyset"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser, fontDir, updatableFontMap)); + FontConfig.Family family = readFamily(parser, fontDir, updatableFontMap); + String name = family.getFallbackName(); + if (name == null || !oemNamedFamilies.containsKey(name)) { + // The OEM customization overrides system named family. Skip if OEM + // customization XML defines the same named family. + families.add(family); + } } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { skip(parser); } } - return new FontConfig(families.toArray(new FontConfig.Family[families.size()]), - aliases.toArray(new FontConfig.Alias[aliases.size()])); + + families.addAll(oemNamedFamilies.values()); + return new FontConfig(families, aliases); } /** - * Reads a family element + * Read family tag in fonts.xml or oem_customization.xml */ public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, @Nullable Map<String, File> updatableFontMap) @@ -94,7 +138,7 @@ public class FontListParser { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); - final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>(); + final List<FontConfig.Font> fonts = new ArrayList<>(); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); @@ -112,20 +156,22 @@ public class FontListParser { intVariant = FontConfig.Family.VARIANT_ELEGANT; } } - return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang, - intVariant); + return new FontConfig.Family(fonts, name, LocaleList.forLanguageTags(lang), intVariant); } /** Matches leading and trailing XML whitespace. */ private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser, String fontDir, + private static FontConfig.Font readFont( + @NonNull XmlPullParser parser, + @NonNull String fontDir, @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { + String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); - List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); + List<FontVariationAxis> axes = new ArrayList<>(); String weightStr = parser.getAttributeValue(null, "weight"); int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); @@ -144,12 +190,37 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - String fontName = findFontFile(sanitizedName, fontDir, updatableFontMap); - return new FontConfig.Font(fontName, index, axes.toArray( - new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); + String updatedName = findUpdatedFontFile(sanitizedName, updatableFontMap); + String filePath; + String originalPath; + if (updatedName != null) { + filePath = updatedName; + originalPath = fontDir + sanitizedName; + } else { + filePath = fontDir + sanitizedName; + originalPath = null; + } + + String varSettings; + if (axes.isEmpty()) { + varSettings = ""; + } else { + varSettings = FontVariationAxis.toFontVariationSettings( + axes.toArray(new FontVariationAxis[0])); + } + + return new FontConfig.Font(new File(filePath), + originalPath == null ? null : new File(originalPath), + new FontStyle( + weight, + isItalic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT + ), + index, + varSettings, + fallbackFor); } - private static String findFontFile(String name, String fontDir, + private static String findUpdatedFontFile(String name, @Nullable Map<String, File> updatableFontMap) { if (updatableFontMap != null) { File updatedFile = updatableFontMap.get(name); @@ -157,7 +228,7 @@ public class FontListParser { return updatedFile.getAbsolutePath(); } } - return fontDir + name; + return null; } private static FontVariationAxis readAxis(XmlPullParser parser) @@ -193,12 +264,12 @@ public class FontListParser { int depth = 1; while (depth > 0) { switch (parser.next()) { - case XmlPullParser.START_TAG: - depth++; - break; - case XmlPullParser.END_TAG: - depth--; - break; + case XmlPullParser.START_TAG: + depth++; + break; + case XmlPullParser.END_TAG: + depth--; + break; } } } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 48b474d6322e..f1866cdceae7 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -45,7 +45,6 @@ import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; -import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -197,7 +196,11 @@ public class Typeface { // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp /** @hide */ public static final int RESOLVE_BY_FONT_TABLE = -1; - private static final String DEFAULT_FAMILY = "sans-serif"; + /** + * The key of the default font family. + * @hide + */ + public static final String DEFAULT_FAMILY = "sans-serif"; // Style value for building typeface. private static final int STYLE_NORMAL = 0; @@ -1139,18 +1142,19 @@ public class Typeface { /** @hide */ @VisibleForTesting - public static void initSystemDefaultTypefaces(Map<String, Typeface> systemFontMap, - Map<String, FontFamily[]> fallbacks, - FontConfig.Alias[] aliases) { + public static void initSystemDefaultTypefaces(Map<String, FontFamily[]> fallbacks, + List<FontConfig.Alias> aliases, + Map<String, Typeface> outSystemFontMap) { for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) { - systemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); + outSystemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue())); } - for (FontConfig.Alias alias : aliases) { - if (systemFontMap.containsKey(alias.getName())) { + for (int i = 0; i < aliases.size(); ++i) { + final FontConfig.Alias alias = aliases.get(i); + if (outSystemFontMap.containsKey(alias.getAliasName())) { continue; // If alias and named family are conflict, use named family. } - final Typeface base = systemFontMap.get(alias.getToName()); + final Typeface base = outSystemFontMap.get(alias.getReferName()); if (base == null) { // The missing target is a valid thing, some configuration don't have font files, // e.g. wear devices. Just skip this alias. @@ -1159,7 +1163,7 @@ public class Typeface { final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); - systemFontMap.put(alias.getName(), newFace); + outSystemFontMap.put(alias.getAliasName(), newFace); } } @@ -1339,11 +1343,11 @@ public class Typeface { /** @hide */ public static void loadPreinstalledSystemFontMap() { - final HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = - SystemFonts.initializePreinstalledFonts(); - initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); - setSystemFontMap(systemFontMap); + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + setSystemFontMap(typefaceMap); } static { diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java index 96dac565eb3d..7e75c5beec8b 100644 --- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java +++ b/graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.internal.graphics.drawable; +package android.graphics.drawable; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; @@ -30,59 +31,71 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RenderNode; -import android.graphics.drawable.Drawable; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; +import android.view.View; import android.view.ViewRootImpl; -import com.android.internal.R; - /** * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state * to SurfaceFlinger. + * + * @hide */ +@SystemApi public final class BackgroundBlurDrawable extends Drawable { - private static final String TAG = BackgroundBlurDrawable.class.getSimpleName(); private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final Aggregator mAggregator; private final RenderNode mRenderNode; private final Paint mPaint = new Paint(); private final Path mRectPath = new Path(); private final float[] mTmpRadii = new float[8]; private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion(); + private Aggregator mAggregator; + // This will be called from a thread pool. private final RenderNode.PositionUpdateListener mPositionUpdateListener = new RenderNode.PositionUpdateListener() { @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { - synchronized (mAggregator) { + if (mAggregator == null) { mBlurRegion.rect.set(left, top, right, bottom); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } else { + synchronized (mAggregator) { + mBlurRegion.rect.set(left, top, right, bottom); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } } } @Override public void positionLost(long frameNumber) { - synchronized (mAggregator) { + if (mAggregator == null) { mBlurRegion.rect.setEmpty(); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } else { + synchronized (mAggregator) { + mBlurRegion.rect.setEmpty(); + mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); + } } } }; - private BackgroundBlurDrawable(Aggregator aggregator) { - mAggregator = aggregator; + @RequiresPermission(android.Manifest.permission.USE_BACKGROUND_BLUR) + public BackgroundBlurDrawable() { mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mPaint.setColor(Color.TRANSPARENT); mRenderNode = new RenderNode("BackgroundBlurDrawable"); mRenderNode.addPositionUpdateListener(mPositionUpdateListener); } + /** + * @hide + */ @Override public void draw(@NonNull Canvas canvas) { if (mRectPath.isEmpty() || !isVisible() || getAlpha() == 0) { @@ -100,6 +113,9 @@ public final class BackgroundBlurDrawable extends Drawable { mPaint.setColor(color); } + /** + * @hide + */ @Override public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); @@ -109,6 +125,9 @@ public final class BackgroundBlurDrawable extends Drawable { return changed; } + /** + * @hide + */ @Override public void setAlpha(int alpha) { mBlurRegion.alpha = alpha / 255f; @@ -139,12 +158,12 @@ public final class BackgroundBlurDrawable extends Drawable { */ public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL, float cornerRadiusBR) { - synchronized (mAggregator) { + maybeRunSynchronized(() -> { mBlurRegion.cornerRadiusTL = cornerRadiusTL; mBlurRegion.cornerRadiusTR = cornerRadiusTR; mBlurRegion.cornerRadiusBL = cornerRadiusBL; mBlurRegion.cornerRadiusBR = cornerRadiusBR; - } + }); updatePath(); invalidateSelf(); } @@ -157,12 +176,13 @@ public final class BackgroundBlurDrawable extends Drawable { } private void updatePath() { - synchronized (mAggregator) { + maybeRunSynchronized(() -> { mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL; mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR; mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL; mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR; - } + }); + mRectPath.reset(); if (getAlpha() == 0 || !isVisible()) { return; @@ -172,19 +192,62 @@ public final class BackgroundBlurDrawable extends Drawable { Path.Direction.CW); } + /** + * @hide + */ @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { throw new IllegalArgumentException("not implemented"); } + /** + * @hide + */ @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } /** + * @hide + */ + @Override + public void onAttached(@NonNull View v) { + super.onAttached(v); + mAggregator = v.getViewRootImpl().getBlurRegionAggregator(); + } + + /** + * @hide + */ + @Override + public void onDetached(@NonNull View v) { + super.onDetached(v); + mAggregator = null; + } + + /** + * The Aggregator is called from the RenderThread to aggregate all blur regions and send them + * to SurfaceFlinger. Since the BackgroundBlurDrawable could be updated at any time from the + * main thread, we need to synchronize the two threads. The BackgroundBlurDrawable may be + * instantiated before the ViewRootImpl is created, i.e. before the Aggregator is created. + * In that case, updates are not synchronized. + */ + private void maybeRunSynchronized(Runnable r) { + if (mAggregator == null) { + r.run(); + } else { + synchronized (mAggregator) { + r.run(); + } + } + } + + /** * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a * message when it's time to propagate them. + * + * @hide */ public static final class Aggregator { @@ -199,16 +262,6 @@ public final class BackgroundBlurDrawable extends Drawable { } /** - * Creates a blur region with default radius. - */ - public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) { - BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this); - drawable.setBlurRadius(context.getResources().getDimensionPixelSize( - R.dimen.default_background_blur_radius)); - return drawable; - } - - /** * Called from RenderThread only, already locked. * @param drawable * @param blurRegion diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 28b3b04b827d..7f22dc271507 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -1465,6 +1465,24 @@ public abstract class Drawable { } /** + * Notifies the drawable that it has been attached. + * + * @param v The view that it is attached to + * @hide + */ + public void onAttached(View v) { + } + + /** + * Notifies the drawable that it has been detached. + * + * @param v The view that it is detached from + * @hide + */ + public void onDetached(View v) { + } + + /** * Sets the source override density for this Drawable. If non-zero, this density is to be used * for any calls to {@link Resources#getDrawableForDensity(int, int, Theme)} or * {@link Resources#getValueForDensity(int, int, TypedValue, boolean)}. diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index f95da82ee07c..1ad6fbe264cb 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -16,18 +16,25 @@ package android.graphics.fonts; +import static android.text.FontConfig.Alias; +import static android.text.FontConfig.Family; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.FontListParser; -import android.text.FontConfig; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Parser for font customization @@ -39,8 +46,27 @@ public class FontCustomizationParser { * Represents a customization XML */ public static class Result { - ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>(); - ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>(); + private final Map<String, Family> mAdditionalNamedFamilies; + private final List<Alias> mAdditionalAliases; + + public Result() { + mAdditionalNamedFamilies = Collections.emptyMap(); + mAdditionalAliases = Collections.emptyList(); + } + + public Result(Map<String, Family> additionalNamedFamilies, + List<Alias> additionalAliases) { + mAdditionalNamedFamilies = additionalNamedFamilies; + mAdditionalAliases = additionalAliases; + } + + public Map<String, Family> getAdditionalNamedFamilies() { + return mAdditionalNamedFamilies; + } + + public List<Alias> getAdditionalAliases() { + return mAdditionalAliases; + } } /** @@ -48,56 +74,67 @@ public class FontCustomizationParser { * * Caller must close the input stream */ - public static Result parse(@NonNull InputStream in, @NonNull String fontDir) - throws XmlPullParserException, IOException { + public static Result parse( + @NonNull InputStream in, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser, fontDir); + return readFamilies(parser, fontDir, updatableFontMap); } - private static void validate(Result result) { - HashSet<String> familyNames = new HashSet<>(); - for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) { - final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i); - final String name = family.getName(); + private static Map<String, Family> validateAndTransformToMap(List<Family> families) { + HashMap<String, Family> namedFamily = new HashMap<>(); + for (int i = 0; i < families.size(); ++i) { + final Family family = families.get(i); + final String name = family.getFallbackName(); if (name == null) { throw new IllegalArgumentException("new-named-family requires name attribute"); } - if (!familyNames.add(name)) { + if (namedFamily.put(name, family) != null) { throw new IllegalArgumentException( "new-named-family requires unique name attribute"); } } + return namedFamily; } - private static Result readFamilies(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { - Result out = new Result(); + private static Result readFamilies( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @Nullable Map<String, File> updatableFontMap + ) throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<>(); + List<Alias> aliases = new ArrayList<>(); parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - readFamily(parser, fontDir, out); + readFamily(parser, fontDir, families, updatableFontMap); } else if (tag.equals("alias")) { - out.mAdditionalAliases.add(FontListParser.readAlias(parser)); + aliases.add(FontListParser.readAlias(parser)); } else { FontListParser.skip(parser); } } - validate(out); - return out; + return new Result(validateAndTransformToMap(families), aliases); } - private static void readFamily(XmlPullParser parser, String fontDir, Result out) + private static void readFamily( + @NonNull XmlPullParser parser, + @NonNull String fontDir, + @NonNull List<Family> out, + @Nullable Map<String, File> updatableFontMap) throws XmlPullParserException, IOException { final String customizationType = parser.getAttributeValue(null, "customizationType"); if (customizationType == null) { throw new IllegalArgumentException("customizationType must be specified"); } if (customizationType.equals("new-named-family")) { - out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir, null)); + out.add(FontListParser.readFamily(parser, fontDir, updatableFontMap)); } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java index 75ea12062929..2f0c26f06df7 100644 --- a/graphics/java/android/graphics/fonts/FontFamily.java +++ b/graphics/java/android/graphics/fonts/FontFamily.java @@ -18,6 +18,7 @@ package android.graphics.fonts; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.text.FontConfig; import com.android.internal.util.Preconditions; @@ -119,7 +120,7 @@ public final class FontFamily { nAddFont(builderPtr, mFonts.get(i).getNativePtr()); } final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback); - final FontFamily family = new FontFamily(mFonts, ptr); + final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr); sFamilyRegistory.registerNativeAllocation(family, ptr); return family; } @@ -138,15 +139,36 @@ public final class FontFamily { } private final ArrayList<Font> mFonts; + private final String mLangTags; + private final int mVariant; private final long mNativePtr; // Use Builder instead. - private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) { + private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) { mFonts = fonts; + mLangTags = langTags; + mVariant = variant; mNativePtr = ptr; } /** + * Returns a BCP-47 compliant language tags associated with this font family. + * @hide + * @return a BCP-47 compliant language tag. + */ + public @Nullable String getLangTags() { + return mLangTags; + } + + /** + * @hide + * @return a family variant + */ + public int getVariant() { + return mVariant; + } + + /** * Returns a font * * @param index an index of the font diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 93b1fcc3ba1f..54167b4b3042 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -23,11 +23,9 @@ import android.graphics.Typeface; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import org.xmlpull.v1.XmlPullParserException; @@ -37,7 +35,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +49,11 @@ public final class SystemFonts { private static final String TAG = "SystemFonts"; private static final String DEFAULT_FAMILY = "sans-serif"; + private static final String FONTS_XML = "/system/etc/fonts.xml"; + private static final String SYSTEM_FONT_DIR = "/system/fonts/"; + private static final String OEM_XML = "/product/etc/fonts_customization.xml"; + private static final String OEM_FONT_DIR = "/product/fonts/"; + private SystemFonts() {} // Do not instansiate. private static final Object LOCK = new Object(); @@ -88,12 +90,10 @@ public final class SystemFonts { } private static @NonNull Set<Font> collectAllFonts() { - final FontCustomizationParser.Result oemCustomization = - readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - Map<String, FontFamily[]> map = new ArrayMap<>(); // TODO: use updated fonts - buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", null /* updatableFontMap */, - oemCustomization, map); + FontConfig fontConfig = getSystemPreinstalledFontConfig(); + Map<String, FontFamily[]> map = buildSystemFallback(fontConfig); + Set<Font> res = new HashSet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { @@ -119,15 +119,16 @@ public final class SystemFonts { @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, @NonNull Map<String, ByteBuffer> cache) { - final String languageTags = xmlFamily.getLanguages(); - final int variant = xmlFamily.getVariant(); + final String languageTags = xmlFamily.getLocaleList().toLanguageTags(); + final int variant = xmlFamily.getTextHeightVariant(); final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>(); - final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>(); + final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = + new ArrayMap<>(); // Collect default fallback and specific fallback fonts. for (final FontConfig.Font font : xmlFamily.getFonts()) { - final String fallbackName = font.getFallbackFor(); + final String fallbackName = font.getFallback(); if (fallbackName == null) { defaultFonts.add(font); } else { @@ -141,19 +142,22 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache); + xmlFamily.getFallbackName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { - final ArrayList<FontConfig.Font> fallback = - specificFallbackFonts.get(fallbackMap.keyAt(i)); + String name = fallbackMap.keyAt(i); + final ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(name); if (fallback == null) { - if (defaultFamily != null) { + String familyName = xmlFamily.getFallbackName(); + if (defaultFamily != null + // do not add myself to the fallback chain. + && (familyName == null || !familyName.equals(name))) { fallbackMap.valueAt(i).add(defaultFamily); } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache); + xmlFamily.getFallbackName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -177,7 +181,7 @@ public final class SystemFonts { FontFamily.Builder b = null; for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); - final String fullPath = fontConfig.getFontName(); + final String fullPath = fontConfig.getFilePath().getAbsolutePath(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -193,11 +197,10 @@ public final class SystemFonts { final Font font; try { font = new Font.Builder(buffer, new File(fullPath), languageTags) - .setWeight(fontConfig.getWeight()) - .setSlant(fontConfig.isItalic() ? FontStyle.FONT_SLANT_ITALIC - : FontStyle.FONT_SLANT_UPRIGHT) - .setTtcIndex(fontConfig.getTtcIndex()) - .setFontVariationSettings(fontConfig.getAxes()) + .setWeight(fontConfig.getStyle().getWeight()) + .setSlant(fontConfig.getStyle().getSlant()) + .setTtcIndex(fontConfig.getIndex()) + .setFontVariationSettings(fontConfig.getFontVariationSettings()) .build(); } catch (IOException e) { throw new RuntimeException(e); // Never reaches here @@ -215,10 +218,11 @@ public final class SystemFonts { private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, @NonNull HashMap<String, ByteBuffer> bufferCache, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { - final String familyName = xmlFamily.getName(); + final String familyName = xmlFamily.getFallbackName(); final FontFamily family = createFontFamily( - familyName, Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache); + familyName, xmlFamily.getFontList(), + xmlFamily.getLocaleList().toLanguageTags(), xmlFamily.getTextHeightVariant(), + bufferCache); if (family == null) { return; } @@ -228,115 +232,104 @@ public final class SystemFonts { } /** - * @see #buildSystemFallback(String, String, Map, FontCustomizationParser.Result, Map) + * Get the updated FontConfig. + * + * @param updatableFontMap a font mapping of updated font files. * @hide */ - @VisibleForTesting - public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, - @NonNull String fontDir, - @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull Map<String, FontFamily[]> fallbackMap) { - return buildSystemFallback(xmlPath, fontDir, null /* updatableFontMap */, - oemCustomization, fallbackMap); + public static @NonNull FontConfig getSystemFontConfig( + @Nullable Map<String, File> updatableFontMap + ) { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, + updatableFontMap); } /** - * Build the system fallback from xml file. - * - * @param xmlPath A full path string to the fonts.xml file. - * @param fontDir A full path string to the system font directory. This must end with - * slash('/'). - * @param updatableFontMap A map from font file name to updated font file path. - * @param fallbackMap An output system fallback map. Caller must pass empty map. - * @return a list of aliases + * Get the system preinstalled FontConfig. * @hide */ - @VisibleForTesting - public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, - @NonNull String fontDir, - @Nullable Map<String, File> updatableFontMap, - @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull Map<String, FontFamily[]> fallbackMap) { - try { - final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir, updatableFontMap); - - final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); - final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); - - final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); - // First traverse families which have a 'name' attribute to create fallback map. - for (final FontConfig.Family xmlFamily : xmlFamilies) { - final String familyName = xmlFamily.getName(); - if (familyName == null) { - continue; - } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); - } + public static @NonNull FontConfig getSystemPreinstalledFontConfig() { + return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null); + } - for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { - appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), - bufferCache, fallbackListMap); - } + /* package */ static @NonNull FontConfig getSystemFontConfigInternal( + @NonNull String fontsXml, + @NonNull String systemFontDir, + @Nullable String oemXml, + @Nullable String productFontDir, + @Nullable Map<String, File> updatableFontMap + ) { + try { + return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir, + updatableFontMap); + } catch (IOException e) { + Log.e(TAG, "Failed to open/read system font configurations.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } catch (XmlPullParserException e) { + Log.e(TAG, "Failed to parse the system font configuration.", e); + return new FontConfig(Collections.emptyList(), Collections.emptyList()); + } + } - // Then, add fallback fonts to the each fallback map. - for (int i = 0; i < xmlFamilies.length; i++) { - final FontConfig.Family xmlFamily = xmlFamilies[i]; - // The first family (usually the sans-serif family) is always placed immediately - // after the primary family in the fallback. - if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); - } + /** + * Build the system fallback from FontConfig. + * @hide + */ + @VisibleForTesting + public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) { + final Map<String, FontFamily[]> fallbackMap = new HashMap<>(); + final HashMap<String, ByteBuffer> bufferCache = new HashMap<>(); + final List<FontConfig.Family> xmlFamilies = fontConfig.getFontFamilies(); + + final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); + // First traverse families which have a 'name' attribute to create fallback map. + for (final FontConfig.Family xmlFamily : xmlFamilies) { + final String familyName = xmlFamily.getFallbackName(); + if (familyName == null) { + continue; } + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); + } - // Build the font map and fallback map. - for (int i = 0; i < fallbackListMap.size(); i++) { - final String fallbackName = fallbackListMap.keyAt(i); - final List<FontFamily> familyList = fallbackListMap.valueAt(i); - final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]); - - fallbackMap.put(fallbackName, families); + // Then, add fallback fonts to the each fallback map. + for (int i = 0; i < xmlFamilies.size(); i++) { + final FontConfig.Family xmlFamily = xmlFamilies.get(i); + // The first family (usually the sans-serif family) is always placed immediately + // after the primary family in the fallback. + if (i == 0 || xmlFamily.getFallbackName() == null) { + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } - - final ArrayList<FontConfig.Alias> list = new ArrayList<>(); - list.addAll(Arrays.asList(fontConfig.getAliases())); - list.addAll(oemCustomization.mAdditionalAliases); - return list.toArray(new FontConfig.Alias[list.size()]); - } catch (IOException | XmlPullParserException e) { - Log.e(TAG, "Failed initialize system fallbacks.", e); - return ArrayUtils.emptyArray(FontConfig.Alias.class); } - } - private static FontCustomizationParser.Result readFontCustomization( - @NonNull String customizeXml, @NonNull String customFontsDir) { - try (FileInputStream f = new FileInputStream(customizeXml)) { - return FontCustomizationParser.parse(f, customFontsDir); - } catch (IOException e) { - return new FontCustomizationParser.Result(); - } catch (XmlPullParserException e) { - Log.e(TAG, "Failed to parse font customization XML", e); - return new FontCustomizationParser.Result(); + // Build the font map and fallback map. + for (int i = 0; i < fallbackListMap.size(); i++) { + final String fallbackName = fallbackListMap.keyAt(i); + final List<FontFamily> familyList = fallbackListMap.valueAt(i); + fallbackMap.put(fallbackName, familyList.toArray(new FontFamily[0])); } + + return fallbackMap; } - /** @hide */ - public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializePreinstalledFonts() { - return initializeSystemFonts(null); + /** + * Build the system Typeface mappings from FontConfig and FallbackMap. + * @hide + */ + @VisibleForTesting + public static Map<String, Typeface> buildSystemTypefaces( + FontConfig fontConfig, + Map<String, FontFamily[]> fallbackMap) { + final HashMap<String, Typeface> result = new HashMap<>(); + Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result); + return result; } - /** @hide */ - public static Pair<FontConfig.Alias[], Map<String, FontFamily[]>> - initializeSystemFonts(@Nullable Map<String, File> updatableFontMap) { - final FontCustomizationParser.Result oemCustomization = - readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - Map<String, FontFamily[]> map = new ArrayMap<>(); - FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - updatableFontMap, oemCustomization, map); + /** + * @hide + */ + public void resetFallbackMapping(Map<String, FontFamily[]> fallbackMap) { synchronized (LOCK) { - sFamilyMap = map; + sFamilyMap = fallbackMap; } - return new Pair(aliases, map); } } diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java new file mode 100644 index 000000000000..14d6626e5e97 --- /dev/null +++ b/keystore/java/android/security/AuthTokenUtils.java @@ -0,0 +1,75 @@ +/* + * 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 android.security; + +import android.annotation.NonNull; +import android.hardware.security.keymint.HardwareAuthToken; +import android.hardware.security.keymint.Timestamp; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @hide This Utils class provides method(s) for AuthToken conversion. + */ +public class AuthTokenUtils { + + private AuthTokenUtils(){ + } + + /** + * Build a HardwareAuthToken from a byte array + * @param array byte array representing an auth token + * @return HardwareAuthToken representation of an auth token + */ + public static @NonNull HardwareAuthToken toHardwareAuthToken(@NonNull byte[] array) { + final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken(); + + // First byte is version, which does not exist in HardwareAuthToken anymore + // Next 8 bytes is the challenge. + hardwareAuthToken.challenge = + ByteBuffer.wrap(array, 1, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the userId + hardwareAuthToken.userId = + ByteBuffer.wrap(array, 9, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the authenticatorId. + hardwareAuthToken.authenticatorId = + ByteBuffer.wrap(array, 17, 8).order(ByteOrder.nativeOrder()).getLong(); + + // while the other fields are in machine byte order, authenticatorType and timestamp + // are in network byte order. + // Next 4 bytes is the authenticatorType. + hardwareAuthToken.authenticatorType = + ByteBuffer.wrap(array, 25, 4).order(ByteOrder.BIG_ENDIAN).getInt(); + // Next 8 bytes is the timestamp. + final Timestamp timestamp = new Timestamp(); + timestamp.milliSeconds = + ByteBuffer.wrap(array, 29, 8).order(ByteOrder.BIG_ENDIAN).getLong(); + hardwareAuthToken.timestamp = timestamp; + + // Last 32 bytes is the mac, 37:69 + hardwareAuthToken.mac = new byte[32]; + System.arraycopy(array, 37 /* srcPos */, + hardwareAuthToken.mac, + 0 /* destPos */, + 32 /* length */); + + return hardwareAuthToken; + } +} diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java new file mode 100644 index 000000000000..1fde2b5412ed --- /dev/null +++ b/keystore/java/android/security/Authorization.java @@ -0,0 +1,78 @@ +/* + * 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 android.security; + +import android.annotation.NonNull; +import android.hardware.security.keymint.HardwareAuthToken; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.security.authorization.IKeystoreAuthorization; +import android.system.keystore2.ResponseCode; +import android.util.Log; + +/** + * @hide This is the client side for IKeystoreAuthorization AIDL. + * It shall only be used by biometric authentication providers and Gatekeeper. + */ +public class Authorization { + private static final String TAG = "KeystoreAuthorization"; + private static IKeystoreAuthorization sIKeystoreAuthorization; + + public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR; + + public Authorization() { + sIKeystoreAuthorization = null; + } + + private static synchronized IKeystoreAuthorization getService() { + if (sIKeystoreAuthorization == null) { + sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface( + ServiceManager.checkService("android.security.authorization")); + } + return sIKeystoreAuthorization; + } + + /** + * Adds an auth token to keystore2. + * + * @param authToken created by Android authenticators. + * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}. + */ + public int addAuthToken(@NonNull HardwareAuthToken authToken) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().addAuthToken(authToken); + return 0; + } catch (RemoteException e) { + Log.w(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } catch (ServiceSpecificException e) { + return e.errorCode; + } + } + + /** + * Add an auth token to Keystore 2.0 in the legacy serialized auth token format. + * @param authToken + * @return 0 if successful or a {@code ResponseCode}. + */ + public int addAuthToken(@NonNull byte[] authToken) { + return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken)); + } + +} diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index c70c986fcd6b..4a67135227dd 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -996,6 +996,7 @@ public class KeyStore { */ public int addAuthToken(byte[] authToken) { try { + new Authorization().addAuthToken(authToken); return mBinder.addAuthToken(authToken); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 8d0e9655f28d..dd4313957f20 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -252,7 +252,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true); // TODO: Synchronize show with the resize onLocationChanged(); - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + if (taskInfo.taskDescription != null) { + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + } if (mListener != null) { mListenerExecutor.execute(() -> { @@ -279,8 +281,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - mTaskInfo.taskDescription = taskInfo.taskDescription; - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + if (taskInfo.taskDescription != null) { + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + } } @Override diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 6bf2e9963777..11a908696903 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -16,12 +16,14 @@ #include "RecordingCanvas.h" -#include "pipeline/skia/FunctorDrawable.h" -#include "VectorDrawable.h" +#include <GrRecordingContext.h> + +#include <experimental/type_traits> #include "SkAndroidFrameworkUtils.h" #include "SkCanvas.h" #include "SkCanvasPriv.h" +#include "SkColor.h" #include "SkData.h" #include "SkDrawShadowInfo.h" #include "SkImage.h" @@ -33,8 +35,8 @@ #include "SkRegion.h" #include "SkTextBlob.h" #include "SkVertices.h" - -#include <experimental/type_traits> +#include "VectorDrawable.h" +#include "pipeline/skia/FunctorDrawable.h" namespace android { namespace uirenderer { @@ -500,7 +502,68 @@ struct DrawWebView final : Op { // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke // onSnapGpuDrawHandler. - void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); } +private: + // Unfortunately WebView does not have complex clip information serialized, and we only perform + // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the + // canvas clip is complex. + static bool needsCompositedLayer(SkCanvas* c) { + if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) { + return false; + } + SkRegion clipRegion; + // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if + // the clip is more complex than a rectangle. + c->temporary_internal_getRgnClip(&clipRegion); + return clipRegion.isComplex(); + } + + mutable SkImageInfo mLayerImageInfo; + mutable sk_sp<SkSurface> mLayerSurface = nullptr; + +public: + void draw(SkCanvas* c, const SkMatrix&) const { + if (needsCompositedLayer(c)) { + // What we do now is create an offscreen surface, sized by the clip bounds. + // We won't apply a clip while drawing - clipping will be performed when compositing the + // surface back onto the original canvas. Note also that we're not using saveLayer + // because the webview functor still doesn't respect the canvas clip stack. + const SkIRect deviceBounds = c->getDeviceClipBounds(); + if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) { + GrRecordingContext* directContext = c->recordingContext(); + mLayerImageInfo = + c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height()); + mLayerSurface = SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes, + mLayerImageInfo, 0, + kTopLeft_GrSurfaceOrigin, nullptr); + } + + SkCanvas* layerCanvas = mLayerSurface->getCanvas(); + + SkAutoCanvasRestore(layerCanvas, true); + layerCanvas->clear(SK_ColorTRANSPARENT); + + // Preserve the transform from the original canvas, but now the clip rectangle is + // anchored at the origin so we need to transform the clipped content to the origin. + SkM44 mat4(c->getLocalToDevice()); + mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop); + layerCanvas->concat(mat4); + layerCanvas->drawDrawable(drawable.get()); + + SkAutoCanvasRestore acr(c, true); + + // Temporarily use an identity transform, because this is just blitting to the parent + // canvas with an offset. + SkMatrix invertedMatrix; + if (!c->getTotalMatrix().invert(&invertedMatrix)) { + ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw"); + return; + } + c->concat(invertedMatrix); + mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr); + } else { + c->drawDrawable(drawable.get()); + } + } }; } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 82b7de4a616b..155f6df7b703 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -53,9 +53,8 @@ public: LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); } - virtual uirenderer::DisplayList finishRecording() override { + virtual void finishRecording(uirenderer::RenderNode*) override { LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList"); - return uirenderer::DisplayList(); } virtual void enableZ(bool enableZ) override { LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ"); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 184b11e735bb..fdfa2883c33f 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -21,7 +21,6 @@ #include <SaveFlags.h> #include <androidfw/ResourceTypes.h> -#include "DisplayList.h" #include "Properties.h" #include "utils/Macros.h" @@ -118,7 +117,7 @@ public: virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode = nullptr) = 0; - [[nodiscard]] virtual uirenderer::DisplayList finishRecording() = 0; + virtual void finishRecording(uirenderer::RenderNode* destination) = 0; virtual void enableZ(bool enableZ) = 0; bool isHighContrastText() const { return uirenderer::Properties::enableHighContrastText; } diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 926e23349a05..855d56ee2e55 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -102,7 +102,7 @@ static void android_view_DisplayListCanvas_finishRecording( CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); - renderNode->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(renderNode); } static void android_view_DisplayListCanvas_drawRenderNode(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong renderNodePtr) { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index e6c6e1094c40..3498f715b455 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -102,12 +102,12 @@ bool SkiaDisplayList::prepareListAndChildren( bool hasBackwardProjectedNodesSubtree = false; for (auto& child : mChildNodes) { - hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); info.hasBackwardProjectedNodes = false; childFn(childNode, observer, info, functorsNeedLayer); + hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 80c014fb99a7..ee7c4d8bb54a 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -55,11 +55,15 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in SkiaCanvas::reset(&mRecorder); } -uirenderer::DisplayList SkiaRecordingCanvas::finishRecording() { +std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() { // close any existing chunks if necessary enableZ(false); mRecorder.restoreToCount(1); - return uirenderer::DisplayList(std::move(mDisplayList)); + return std::move(mDisplayList); +} + +void SkiaRecordingCanvas::finishRecording(uirenderer::RenderNode* destination) { + destination->setStagingDisplayList(uirenderer::DisplayList(finishRecording())); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 32c179191ec6..8d7a21a732dd 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -39,11 +39,12 @@ public: } virtual void resetRecording(int width, int height, - uirenderer::RenderNode* renderNode) override { + uirenderer::RenderNode* renderNode = nullptr) override { initDisplayList(renderNode, width, height); } - virtual uirenderer::DisplayList finishRecording() override; + virtual void finishRecording(uirenderer::RenderNode* destination) override; + std::unique_ptr<SkiaDisplayList> finishRecording(); virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp index fd331333d38a..43df4a0b1576 100644 --- a/libs/hwui/tests/common/TestListViewSceneBase.cpp +++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp @@ -70,7 +70,7 @@ void TestListViewSceneBase::doFrame(int frameNr) { // draw it to parent DisplayList canvas->drawRenderNode(mListItems[ci].get()); } - mListView->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(mListView.get()); } } // namespace test diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ba6e8ee290bc..cf8fc82abb4a 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -159,14 +159,6 @@ public: renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform); - template <class CanvasType> - static std::unique_ptr<DisplayList> createDisplayList( - int width, int height, std::function<void(CanvasType& canvas)> canvasCallback) { - CanvasType canvas(width, height); - canvasCallback(canvas); - return std::unique_ptr<DisplayList>(canvas.finishRecording()); - } - static sp<RenderNode> createNode( int left, int top, int right, int bottom, std::function<void(RenderProperties& props, Canvas& canvas)> setup) { @@ -177,7 +169,7 @@ public: std::unique_ptr<Canvas> canvas( Canvas::create_recording_canvas(props.getWidth(), props.getHeight())); setup(props, *canvas.get()); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } node->setPropertyFieldsDirty(0xFFFFFFFF); return node; @@ -203,7 +195,7 @@ public: std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node)); contentCallback(*canvas.get()); - node.setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(&node); } static sp<RenderNode> createSkiaNode( @@ -226,7 +218,7 @@ public: new skiapipeline::SkiaRecordingCanvas(nullptr, props.getWidth(), props.getHeight())); setup(props, *canvas.get()); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } node->setPropertyFieldsDirty(0xFFFFFFFF); TestUtils::syncHierarchyPropertiesAndDisplayList(node); diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index 0795d13f441b..4271d2f04b88 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -55,6 +55,6 @@ public: TestUtils::drawUtf8ToCanvas(canvas.get(), text, paint, 0, 100 * (i + 2)); } - container->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(container.get()); } }; diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 1b0a07a98b3f..c6219c485b85 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -210,7 +210,7 @@ private: overlay->stagingProperties().getHeight(), overlay.get())); canvas->drawColor((curFrame % 150) << 24, SkBlendMode::kSrcOver); - overlay->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(overlay.get()); cardcanvas->drawRenderNode(overlay.get()); } else { // re-recording image node's canvas, animating ColorFilter @@ -223,11 +223,11 @@ private: paint.setColorFilter(filter); sk_sp<Bitmap> bitmap = mCachedBitmaps[ci]; canvas->drawBitmap(*bitmap, 0, 0, &paint); - image->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(image.get()); cardcanvas->drawRenderNode(image.get()); } - card->setStagingDisplayList(cardcanvas->finishRecording()); + cardcanvas->finishRecording(card.get()); } }; diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index ade1ddd3f703..9cd10759a834 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -24,27 +24,28 @@ using namespace android; using namespace android::uirenderer; +using namespace android::uirenderer::skiapipeline; -void BM_DisplayList_alloc(benchmark::State& benchState) { +void BM_SkiaDisplayList_alloc(benchmark::State& benchState) { while (benchState.KeepRunning()) { auto displayList = new skiapipeline::SkiaDisplayList(); benchmark::DoNotOptimize(displayList); delete displayList; } } -BENCHMARK(BM_DisplayList_alloc); +BENCHMARK(BM_SkiaDisplayList_alloc); -void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) { +void BM_SkiaDisplayList_alloc_theoretical(benchmark::State& benchState) { while (benchState.KeepRunning()) { auto displayList = new char[sizeof(skiapipeline::SkiaDisplayList)]; benchmark::DoNotOptimize(displayList); delete[] displayList; } } -BENCHMARK(BM_DisplayList_alloc_theoretical); +BENCHMARK(BM_SkiaDisplayList_alloc_theoretical); -void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_empty(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -53,10 +54,10 @@ void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_empty); +BENCHMARK(BM_SkiaDisplayListCanvas_record_empty); -void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_saverestore(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -69,10 +70,10 @@ void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_saverestore); +BENCHMARK(BM_SkiaDisplayListCanvas_record_saverestore); -void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_translate(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -82,7 +83,7 @@ void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_translate); +BENCHMARK(BM_SkiaDisplayListCanvas_record_translate); /** * Simulate a simple view drawing a background, overlapped by an image. @@ -90,8 +91,8 @@ BENCHMARK(BM_DisplayListCanvas_record_translate); * Note that the recording commands are intentionally not perfectly efficient, as the * View system frequently produces unneeded save/restores. */ -void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) { - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); +void BM_SkiaDisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) { + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); Paint rectPaint; @@ -114,14 +115,14 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView); +BENCHMARK(BM_SkiaDisplayListCanvas_record_simpleBitmapView); -void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { +void BM_SkiaDisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); }); - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); + auto canvas = std::make_unique<SkiaRecordingCanvas>(nullptr, 100, 100); static_cast<void>(canvas->finishRecording()); while (benchState.KeepRunning()) { @@ -146,4 +147,4 @@ void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { static_cast<void>(canvas->finishRecording()); } } -BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); +BENCHMARK(BM_SkiaDisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp index dd3f737abfa9..6aed251481bf 100644 --- a/libs/hwui/tests/microbench/RenderNodeBench.cpp +++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp @@ -35,25 +35,12 @@ BENCHMARK(BM_RenderNode_create); void BM_RenderNode_recordSimple(benchmark::State& state) { sp<RenderNode> node = new RenderNode(); std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); - static_cast<void>(canvas->finishRecording()); + canvas->finishRecording(node.get()); while (state.KeepRunning()) { canvas->resetRecording(100, 100, node.get()); canvas->drawColor(0x00000000, SkBlendMode::kSrcOver); - node->setStagingDisplayList(canvas->finishRecording()); + canvas->finishRecording(node.get()); } } BENCHMARK(BM_RenderNode_recordSimple); - -void BM_RenderNode_recordSimpleWithReuse(benchmark::State& state) { - sp<RenderNode> node = new RenderNode(); - std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); - static_cast<void>(canvas->finishRecording()); - - while (state.KeepRunning()) { - canvas->resetRecording(100, 100, node.get()); - canvas->drawColor(0x00000000, SkBlendMode::kSrcOver); - canvas->finishRecording().clear(node.get()); - } -} -BENCHMARK(BM_RenderNode_recordSimpleWithReuse);
\ No newline at end of file diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 801a294b5648..3d5aca4bf05a 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -38,13 +38,12 @@ TEST(SkiaDisplayList, create) { } TEST(SkiaDisplayList, reset) { - DisplayList displayList; + std::unique_ptr<SkiaDisplayList> skiaDL; { SkiaRecordingCanvas canvas{nullptr, 1, 1}; canvas.drawColor(0, SkBlendMode::kSrc); - displayList = canvas.finishRecording(); + skiaDL = canvas.finishRecording(); } - SkiaDisplayList* skiaDL = displayList.asSkiaDl(); SkCanvas dummyCanvas; RenderNodeDrawable drawable(nullptr, &dummyCanvas); diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl index 52d952c5066d..e55678d90078 100644 --- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl +++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl @@ -20,6 +20,7 @@ import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; import android.media.metrics.PlaybackStateEvent; +import android.media.metrics.TrackChangeEvent; /** * Interface to the playback manager service. @@ -31,4 +32,5 @@ interface IPlaybackMetricsManager { void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId); void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId); void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId); + void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId); }
\ No newline at end of file diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java index 63a50ae31134..f48ffe7f3b22 100644 --- a/media/java/android/media/metrics/PlaybackMetricsManager.java +++ b/media/java/android/media/metrics/PlaybackMetricsManager.java @@ -73,6 +73,18 @@ public class PlaybackMetricsManager { } /** + * Reports track change event. + * @hide + */ + public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) { + try { + mService.reportTrackChangeEvent(sessionId, event, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Creates a playback session. */ public PlaybackSession createSession() { diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java index 061e66532d55..0a77516a0b8c 100644 --- a/media/java/android/media/metrics/PlaybackSession.java +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -71,6 +71,13 @@ public final class PlaybackSession implements AutoCloseable { mManager.reportPlaybackStateEvent(mId, event); } + /** + * Reports track change event. + */ + public void reportTrackChangeEvent(TrackChangeEvent event) { + mManager.reportTrackChangeEvent(mId, event); + } + public @NonNull String getId() { return mId; } diff --git a/media/java/android/media/metrics/TrackChangeEvent.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl new file mode 100644 index 000000000000..8fbe4a9eb66c --- /dev/null +++ b/media/java/android/media/metrics/TrackChangeEvent.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.media.metrics; + +parcelable TrackChangeEvent; diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java new file mode 100644 index 000000000000..fff0e36c062a --- /dev/null +++ b/media/java/android/media/metrics/TrackChangeEvent.java @@ -0,0 +1,480 @@ +/* + * 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 android.media.metrics; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Playback track change event. + * @hide + */ +public final class TrackChangeEvent implements Parcelable { + public static final int TRACK_STATE_OFF = 0; + public static final int TRACK_STATE_ON = 1; + + public static final int TRACK_CHANGE_REASON_UNKNOWN = 0; + public static final int TRACK_CHANGE_REASON_OTHER = 1; + public static final int TRACK_CHANGE_REASON_INITIAL = 2; + public static final int TRACK_CHANGE_REASON_MANUAL = 3; + public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4; + + public static final int TRACK_TYPE_AUDIO = 0; + public static final int TRACK_TYPE_VIDEO = 1; + public static final int TRACK_TYPE_TEXT = 2; + + private final int mState; + private final int mReason; + private final @Nullable String mContainerMimeType; + private final @Nullable String mSampleMimeType; + private final @Nullable String mCodecName; + private final int mBitrate; + private final long mTimeSincePlaybackCreatedMillis; + private final int mType; + private final @Nullable String mLanguage; + private final @Nullable String mLanguageRegion; + private final int mChannelCount; + private final int mSampleRate; + private final int mWidth; + private final int mHeight; + + + + /** @hide */ + @IntDef(prefix = "TRACK_STATE_", value = { + TRACK_STATE_OFF, + TRACK_STATE_ON + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackState {} + + /** @hide */ + @IntDef(prefix = "TRACK_CHANGE_REASON_", value = { + TRACK_CHANGE_REASON_UNKNOWN, + TRACK_CHANGE_REASON_OTHER, + TRACK_CHANGE_REASON_INITIAL, + TRACK_CHANGE_REASON_MANUAL, + TRACK_CHANGE_REASON_ADAPTIVE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackChangeReason {} + + /** @hide */ + @IntDef(prefix = "TRACK_TYPE_", value = { + TRACK_TYPE_AUDIO, + TRACK_TYPE_VIDEO, + TRACK_TYPE_TEXT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TrackType {} + + public TrackChangeEvent( + int state, + int reason, + @Nullable String containerMimeType, + @Nullable String sampleMimeType, + @Nullable String codecName, + int bitrate, + long timeSincePlaybackCreatedMillis, + int type, + @Nullable String language, + @Nullable String languageRegion, + int channelCount, + int sampleRate, + int width, + int height) { + this.mState = state; + this.mReason = reason; + this.mContainerMimeType = containerMimeType; + this.mSampleMimeType = sampleMimeType; + this.mCodecName = codecName; + this.mBitrate = bitrate; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + this.mType = type; + this.mLanguage = language; + this.mLanguageRegion = languageRegion; + this.mChannelCount = channelCount; + this.mSampleRate = sampleRate; + this.mWidth = width; + this.mHeight = height; + } + + @TrackState + public int getTrackState() { + return mState; + } + + @TrackChangeReason + public int getTrackChangeReason() { + return mReason; + } + + public @Nullable String getContainerMimeType() { + return mContainerMimeType; + } + + public @Nullable String getSampleMimeType() { + return mSampleMimeType; + } + + public @Nullable String getCodecName() { + return mCodecName; + } + + public int getBitrate() { + return mBitrate; + } + + public long getTimeSincePlaybackCreatedMillis() { + return mTimeSincePlaybackCreatedMillis; + } + + @TrackType + public int getTrackType() { + return mType; + } + + public @Nullable String getLanguage() { + return mLanguage; + } + + public @Nullable String getLanguageRegion() { + return mLanguageRegion; + } + + public int getChannelCount() { + return mChannelCount; + } + + public int getSampleRate() { + return mSampleRate; + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + int flg = 0; + if (mContainerMimeType != null) flg |= 0x4; + if (mSampleMimeType != null) flg |= 0x8; + if (mCodecName != null) flg |= 0x10; + if (mLanguage != null) flg |= 0x100; + if (mLanguageRegion != null) flg |= 0x200; + dest.writeInt(flg); + dest.writeInt(mState); + dest.writeInt(mReason); + if (mContainerMimeType != null) dest.writeString(mContainerMimeType); + if (mSampleMimeType != null) dest.writeString(mSampleMimeType); + if (mCodecName != null) dest.writeString(mCodecName); + dest.writeInt(mBitrate); + dest.writeLong(mTimeSincePlaybackCreatedMillis); + dest.writeInt(mType); + if (mLanguage != null) dest.writeString(mLanguage); + if (mLanguageRegion != null) dest.writeString(mLanguageRegion); + dest.writeInt(mChannelCount); + dest.writeInt(mSampleRate); + dest.writeInt(mWidth); + dest.writeInt(mHeight); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + /* package-private */ TrackChangeEvent(@NonNull Parcel in) { + int flg = in.readInt(); + int state = in.readInt(); + int reason = in.readInt(); + String containerMimeType = (flg & 0x4) == 0 ? null : in.readString(); + String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString(); + String codecName = (flg & 0x10) == 0 ? null : in.readString(); + int bitrate = in.readInt(); + long timeSincePlaybackCreatedMillis = in.readLong(); + int type = in.readInt(); + String language = (flg & 0x100) == 0 ? null : in.readString(); + String languageRegion = (flg & 0x200) == 0 ? null : in.readString(); + int channelCount = in.readInt(); + int sampleRate = in.readInt(); + int width = in.readInt(); + int height = in.readInt(); + + this.mState = state; + this.mReason = reason; + this.mContainerMimeType = containerMimeType; + this.mSampleMimeType = sampleMimeType; + this.mCodecName = codecName; + this.mBitrate = bitrate; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + this.mType = type; + this.mLanguage = language; + this.mLanguageRegion = languageRegion; + this.mChannelCount = channelCount; + this.mSampleRate = sampleRate; + this.mWidth = width; + this.mHeight = height; + } + + public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR = + new Parcelable.Creator<TrackChangeEvent>() { + @Override + public TrackChangeEvent[] newArray(int size) { + return new TrackChangeEvent[size]; + } + + @Override + public TrackChangeEvent createFromParcel(@NonNull Parcel in) { + return new TrackChangeEvent(in); + } + }; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + @Override + public String toString() { + return "TrackChangeEvent { " + + "state = " + mState + ", " + + "reason = " + mReason + ", " + + "containerMimeType = " + mContainerMimeType + ", " + + "sampleMimeType = " + mSampleMimeType + ", " + + "codecName = " + mCodecName + ", " + + "bitrate = " + mBitrate + ", " + + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " + + "type = " + mType + ", " + + "language = " + mLanguage + ", " + + "languageRegion = " + mLanguageRegion + ", " + + "channelCount = " + mChannelCount + ", " + + "sampleRate = " + mSampleRate + ", " + + "width = " + mWidth + ", " + + "height = " + mHeight + + " }"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrackChangeEvent that = (TrackChangeEvent) o; + return mState == that.mState + && mReason == that.mReason + && Objects.equals(mContainerMimeType, that.mContainerMimeType) + && Objects.equals(mSampleMimeType, that.mSampleMimeType) + && Objects.equals(mCodecName, that.mCodecName) + && mBitrate == that.mBitrate + && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis + && mType == that.mType + && Objects.equals(mLanguage, that.mLanguage) + && Objects.equals(mLanguageRegion, that.mLanguageRegion) + && mChannelCount == that.mChannelCount + && mSampleRate == that.mSampleRate + && mWidth == that.mWidth + && mHeight == that.mHeight; + } + + @Override + public int hashCode() { + return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName, + mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion, + mChannelCount, mSampleRate, mWidth, mHeight); + } + + /** + * A builder for {@link TrackChangeEvent} + */ + public static final class Builder { + // TODO: check track type for the setters. + private int mState; + private int mReason; + private @Nullable String mContainerMimeType; + private @Nullable String mSampleMimeType; + private @Nullable String mCodecName; + private int mBitrate; + private long mTimeSincePlaybackCreatedMillis; + private int mType; + private @Nullable String mLanguage; + private @Nullable String mLanguageRegion; + private int mChannelCount; + private int mSampleRate; + private int mWidth; + private int mHeight; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @hide + */ + public Builder(int type) { + mType = type; + } + + public @NonNull Builder setTrackState(@TrackState int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mState = value; + return this; + } + + public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mReason = value; + return this; + } + + public @NonNull Builder setContainerMimeType(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mContainerMimeType = value; + return this; + } + + public @NonNull Builder setSampleMimeType(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mSampleMimeType = value; + return this; + } + + public @NonNull Builder setCodecName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mCodecName = value; + return this; + } + + public @NonNull Builder setBitrate(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mBitrate = value; + return this; + } + + public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mTimeSincePlaybackCreatedMillis = value; + return this; + } + + public @NonNull Builder setTrackType(@TrackType int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mType = value; + return this; + } + + public @NonNull Builder setLanguage(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; + mLanguage = value; + return this; + } + + public @NonNull Builder setLanguageRegion(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x200; + mLanguageRegion = value; + return this; + } + + public @NonNull Builder setChannelCount(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x400; + mChannelCount = value; + return this; + } + + public @NonNull Builder setSampleRate(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x800; + mSampleRate = value; + return this; + } + + public @NonNull Builder setWidth(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1000; + mWidth = value; + return this; + } + + public @NonNull Builder setHeight(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2000; + mHeight = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TrackChangeEvent build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4000; // Mark builder used + + TrackChangeEvent o = new TrackChangeEvent( + mState, + mReason, + mContainerMimeType, + mSampleMimeType, + mCodecName, + mBitrate, + mTimeSincePlaybackCreatedMillis, + mType, + mLanguage, + mLanguageRegion, + mChannelCount, + mSampleRate, + mWidth, + mHeight); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4000) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } +} diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp index f18917cd2ebb..ca23616ca588 100644 --- a/packages/SettingsLib/SettingsSpinner/Android.bp +++ b/packages/SettingsLib/SettingsSpinner/Android.bp @@ -4,6 +4,10 @@ android_library { srcs: ["src/**/*.java"], resource_dirs: ["res"], + static_libs: [ + "androidx.preference_preference", + ], + sdk_version: "system_current", min_sdk_version: "21", } diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml new file mode 100644 index 000000000000..7d5b6db6f6d6 --- /dev/null +++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp"> + + <com.android.settingslib.widget.settingsspinner.SettingsSpinner + android:id="@+id/spinner" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true"/> +</RelativeLayout> + diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java new file mode 100644 index 000000000000..154a0f44788d --- /dev/null +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java @@ -0,0 +1,128 @@ +/* + * 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.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import com.android.settingslib.widget.settingsspinner.SettingsSpinner; +import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; + +/** + * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for + * both view and drop down view of the Spinner. + */ +public class SettingsSpinnerPreference extends Preference { + + private SettingsSpinnerAdapter mAdapter; + private AdapterView.OnItemSelectedListener mListener; + private int mPosition; //Default 0 for internal shard storage. + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + * @param defStyle An attribute in the current theme that contains a reference to a style + * resource that supplies default values for the view. Can be 0 to not + * look for defaults. + */ + public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setLayoutResource(R.layout.settings_spinner_preference); + } + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + */ + public SettingsSpinnerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.settings_spinner_preference); + } + + /** + * Constructor to create a preference. + * + * @param context The Context this is associated with. + */ + public SettingsSpinnerPreference(Context context) { + this(context, null); + } + + /** Sets adapter of the spinner. */ + public <T extends SettingsSpinnerAdapter> void setAdapter(T adapter) { + mAdapter = adapter; + notifyChanged(); + } + + /** Sets item selection listener of the spinner. */ + public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) { + mListener = listener; + } + + /** Gets selected item of the spinner. */ + public Object getSelectedItem() { + return mAdapter == null ? null : mAdapter.getItem(mPosition); + } + + /** Gets selection position of the spinner */ + public void setSelection(int position) { + if (mPosition == position) { + return; + } + mPosition = position; + notifyChanged(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner); + spinner.setAdapter(mAdapter); + spinner.setSelection(mPosition); + spinner.setOnItemSelectedListener(mOnSelectedListener); + } + + private final AdapterView.OnItemSelectedListener mOnSelectedListener = + new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + if (mPosition == position) return; + mPosition = position; + if (mListener != null) { + mListener.onItemSelected(parent, view, position, id); + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + if (mListener != null) { + mListener.onNothingSelected(parent); + } + } + }; +} diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java index f9aedd9c07a4..a8ca0d8664f3 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java @@ -17,6 +17,9 @@ package com.android.settingslib.widget.settingsspinner; import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import com.android.settingslib.widget.R; @@ -26,6 +29,11 @@ import com.android.settingslib.widget.R; */ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { + private static final int DEFAULT_RESOURCE = R.layout.settings_spinner_view; + private static final int DFAULT_DROPDOWN_RESOURCE = + android.R.layout.simple_spinner_dropdown_item; + private final LayoutInflater mDefaultInflater; + /** * Constructs a new SettingsSpinnerAdapter with the given context. * And it customizes title bar with a settings style. @@ -34,7 +42,24 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * access the current theme, resources, etc. */ public SettingsSpinnerAdapter(Context context) { - super(context, R.layout.settings_spinner_view); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + super(context, DEFAULT_RESOURCE); + + setDropDownViewResource(DFAULT_DROPDOWN_RESOURCE); + mDefaultInflater = LayoutInflater.from(context); + } + + /** + * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view. + */ + public View getDefaultView(int position, View convertView, ViewGroup parent) { + return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */); + } + + /** + * In overridded {@link #getDropDownView(int, View, ViewGroup)}, use this method to get default + * drop down view. + */ + public View getDefaultDropDownView(int position, View convertView, ViewGroup parent) { + return mDefaultInflater.inflate(DFAULT_DROPDOWN_RESOURCE, parent, false /* attachToRoot */); } } diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index f6f9dba784a0..92e32d96cdf3 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -38,6 +38,7 @@ android_test { "androidx.test.espresso.core", "mockito-target-minus-junit4", "truth-prebuilt", + "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", ], diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java new file mode 100644 index 000000000000..53a382a9ebf6 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java @@ -0,0 +1,92 @@ +/* + * 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 static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import com.android.settingslib.widget.settingsspinner.SettingsSpinner; +import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class SettingsSpinnerPreferenceTest { + + private Context mContext; + private PreferenceViewHolder mViewHolder; + private SettingsSpinner mSpinner; + private SettingsSpinnerPreference mSpinnerPreference; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mSpinnerPreference = new SettingsSpinnerPreference(mContext); + final LayoutInflater inflater = LayoutInflater.from(mContext); + final View rootView = inflater.inflate(mSpinnerPreference.getLayoutResource(), + new LinearLayout(mContext), false /* attachToRoot */); + mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView); + mSpinner = (SettingsSpinner) mViewHolder.findViewById(R.id.spinner); + } + + @Test + public void onBindViewHolder_noSetSelection_getDefaultItem() { + final List<CharSequence> list = new ArrayList<>(); + list.add("TEST1"); + list.add("TEST2"); + list.add("TEST3"); + final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext); + adapter.addAll(list); + mSpinnerPreference.setAdapter(adapter); + + mSpinnerPreference.onBindViewHolder(mViewHolder); + + assertThat(adapter).isEqualTo(mSpinner.getAdapter()); + assertThat(mSpinnerPreference.getSelectedItem()) + .isEqualTo(mSpinner.getAdapter().getItem(0)); + } + + @Test + public void onBindViewHolder_setSelection_getSelectedItem() { + final List<CharSequence> list = new ArrayList<>(); + list.add("TEST1"); + list.add("TEST2"); + list.add("TEST3"); + final SettingsSpinnerAdapter adapter = new SettingsSpinnerAdapter(mContext); + adapter.addAll(list); + mSpinnerPreference.setAdapter(adapter); + mSpinnerPreference.setSelection(1); + + mSpinnerPreference.onBindViewHolder(mViewHolder); + + assertThat(mSpinnerPreference.getSelectedItem()) + .isEqualTo(mSpinner.getAdapter().getItem(1)); + } +} diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index d278c5974ae6..86aa214b04bf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -83,7 +83,7 @@ public class SystemSettingsValidators { return value == null || value.length() < MAX_LENGTH; } }); - VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f)); + VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f)); VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put( System.DISPLAY_COLOR_MODE, diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index c873e30d2107..546642db881a 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -1,11 +1,15 @@ +// used both for the android_app and android_library +shell_srcs = ["src/**/*.java",":dumpstate_aidl"] +shell_static_libs = ["androidx.legacy_legacy-support-v4"] + android_app { name: "Shell", defaults: ["platform_app_defaults"], - srcs: ["src/**/*.java",":dumpstate_aidl"], + srcs: shell_srcs, aidl: { include_dirs: ["frameworks/native/cmds/dumpstate/binder"], }, - static_libs: ["androidx.legacy_legacy-support-v4"], + static_libs: shell_static_libs, platform_apis: true, certificate: "platform", privileged: true, @@ -13,3 +17,18 @@ android_app { include_filter: ["com.android.shell.*"], }, } + +// A library for product type like auto to create a new shell package +// with product specific permissions. +android_library { + name: "Shell-package-library", + defaults: ["platform_app_defaults"], + srcs: shell_srcs, + aidl: { + include_dirs: ["frameworks/native/cmds/dumpstate/binder"], + }, + resource_dirs: ["res"], + static_libs: shell_static_libs, + platform_apis: true, + manifest: "AndroidManifest.xml", +} diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e036d87f2492..39fbb34f4877 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -116,6 +116,7 @@ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> <uses-permission android:name="android.permission.INPUT_CONSUMER" /> + <uses-permission android:name="android.permission.USE_BACKGROUND_BLUR" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml index 3790378ff3ae..8e3768697f6a 100644 --- a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml +++ b/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml @@ -17,9 +17,8 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/media_seamless_border"> <item android:id="@android:id/background"> - <shape - android:color="@android:color/transparent"> - <stroke android:width="1dp" android:color="@color/media_seamless_border"/> + <shape android:shape="rectangle"> + <solid android:color="@color/media_seamless_border" /> <corners android:radius="24dp"/> </shape> </item> diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml index 656d2e4a33ff..6ed3a0aed091 100644 --- a/packages/SystemUI/res/drawable/qs_media_background.xml +++ b/packages/SystemUI/res/drawable/qs_media_background.xml @@ -17,4 +17,4 @@ <com.android.systemui.media.IlluminationDrawable xmlns:systemui="http://schemas.android.com/apk/res-auto" systemui:highlight="15" - systemui:cornerRadius="?android:attr/dialogCornerRadius" />
\ No newline at end of file + systemui:cornerRadius="@dimen/notification_corner_radius" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 170f2c4e0bea..6b4270531d0b 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -64,7 +64,7 @@ </FrameLayout> <!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain - with the album art and the title, and must as a group appear at the end of that chain. This is + with the album art, and must as a group appear at the end of that chain. This is accomplished by having all actions appear in a LTR chain within the parent, and then biasing it to the right side, then this barrier is used to bound the text views. --> <androidx.constraintlayout.widget.Barrier @@ -72,10 +72,10 @@ android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintTop_toBottomOf="@id/header_artist" app:barrierDirection="start" app:constraint_referenced_ids="action0,action1,action2,action3,action4" - /> + app:layout_constraintHorizontal_bias="0" /> <ImageButton android:id="@+id/action0" @@ -111,42 +111,46 @@ <ImageView android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" - android:layout_height="@dimen/qs_media_album_size" /> + android:layout_height="@dimen/qs_media_album_size" + android:layout_gravity="center_vertical" /> <!-- Seamless Output Switcher --> <LinearLayout android:id="@+id/media_seamless" android:layout_width="0dp" android:layout_height="wrap_content" - android:foreground="@drawable/qs_media_seamless_background" - android:background="@drawable/qs_media_light_source" android:orientation="horizontal" - android:forceHasOverlappingRendering="false" - android:paddingStart="12dp" - android:paddingTop="6dp" - android:paddingEnd="12dp" - android:paddingBottom="6dp"> - - <ImageView - android:id="@+id/media_seamless_image" - android:layout_width="@dimen/qs_seamless_icon_size" - android:layout_height="@dimen/qs_seamless_icon_size" - android:layout_marginEnd="8dp" - android:layout_gravity="center_vertical" - android:tint="@color/media_primary_text" - android:src="@*android:drawable/ic_media_seamless" /> - - <TextView - android:id="@+id/media_seamless_text" + android:gravity="center_vertical|end" + android:forceHasOverlappingRendering="false"> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:singleLine="true" - android:text="@*android:string/ext_media_seamless_action" - android:textColor="@color/media_primary_text" - android:textDirection="locale" - android:textSize="14sp" /> + android:foreground="@drawable/qs_media_seamless_background" + android:background="@drawable/qs_media_light_source" + android:orientation="horizontal" + android:padding="6dp" + android:contentDescription="@string/quick_settings_media_device_label"> + <ImageView + android:id="@+id/media_seamless_image" + android:layout_width="@dimen/qs_seamless_icon_size" + android:layout_height="@dimen/qs_seamless_icon_size" + android:layout_gravity="center" + android:tint="@color/media_primary_text" + android:src="@*android:drawable/ic_media_seamless" /> + <TextView + android:visibility="gone" + android:id="@+id/media_seamless_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="8dp" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:singleLine="true" + android:text="@*android:string/ext_media_seamless_action" + android:textColor="@color/media_primary_text" + android:textDirection="locale" + android:textSize="14sp" /> + </LinearLayout> </LinearLayout> <ImageView @@ -206,8 +210,9 @@ <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:tint="@color/media_primary_text" - android:layout_width="@dimen/qs_media_icon_size" - android:layout_height="@dimen/qs_media_icon_size" /> + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_margin="6dp" /> <!-- Constraints are set here as they are the same regardless of host --> <TextView diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 672d2f61e18b..c4cf440d4961 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -73,7 +73,7 @@ <color name="media_divider">#85ffffff</color> <!-- Biometric dialog colors --> - <color name="biometric_dialog_gray">#ff888888</color> + <color name="biometric_dialog_gray">#ffcccccc</color> <color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal --> <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ae55d95b6b52..d104d17f37b7 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1180,10 +1180,9 @@ <dimen name="new_qs_vertical_margin">8dp</dimen> <!-- Size of media cards in the QSPanel carousel --> - <dimen name="qs_media_width">350dp</dimen> <dimen name="qs_media_padding">16dp</dimen> <dimen name="qs_media_panel_outer_padding">16dp</dimen> - <dimen name="qs_media_album_size">52dp</dimen> + <dimen name="qs_media_album_size">120dp</dimen> <dimen name="qs_media_icon_size">16dp</dimen> <dimen name="qs_center_guideline_padding">10dp</dimen> <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml new file mode 100644 index 000000000000..db8bfec2bac5 --- /dev/null +++ b/packages/SystemUI/res/values/flags.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<resources> + <bool name="are_flags_overrideable">true</bool> + + <bool name="flag_notification_pipeline2">false</bool> + <bool name="flag_notification_pipeline2_rendering">false</bool> + + <!-- b/171917882 --> + <bool name="flag_notification_twocolumn">false</bool> +</resources> diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml index ee958f28c51b..f834d6df15c2 100644 --- a/packages/SystemUI/res/xml/media_collapsed.xml +++ b/packages/SystemUI/res/xml/media_collapsed.xml @@ -22,36 +22,39 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" android:layout_marginStart="18dp" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" - app:layout_constraintStart_toStartOf="parent" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@id/album_art" /> <Constraint android:id="@+id/app_name" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_center_guideline_padding" - android:layout_marginStart="10dp" - android:layout_marginTop="20dp" - app:layout_constraintTop_toTopOf="parent" + android:layout_marginStart="8dp" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/icon" - app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toStartOf="@id/media_seamless" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constrainedWidth="true" app:layout_constraintHorizontal_bias="0" /> <Constraint android:id="@+id/media_seamless" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constrainedWidth="true" - app:layout_constraintWidth_min="60dp" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintStart_toEndOf="@id/app_name" + app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintHorizontal_bias="1" - android:layout_marginTop="@dimen/qs_media_panel_outer_padding" - android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + app:layout_constrainedWidth="true" + app:layout_constraintWidth_min="48dp" + app:layout_constraintHeight_min="48dp" + android:layout_marginEnd="@dimen/qs_center_guideline_padding" android:layout_marginStart="@dimen/qs_center_guideline_padding" /> @@ -64,22 +67,23 @@ android:alpha="0.5" android:visibility="gone" app:layout_constraintHorizontal_bias="1" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toEndOf="parent" /> <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" android:layout_height="@dimen/qs_media_album_size" - android:layout_marginTop="16dp" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" - android:layout_marginBottom="24dp" - app:layout_constraintTop_toBottomOf="@id/icon" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/media_action_barrier" + app:layout_constraintHorizontal_bias="0" /> <!-- Song name --> @@ -87,13 +91,14 @@ android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="17dp" - android:layout_marginStart="16dp" + android:layout_marginTop="@dimen/qqs_media_spacing" + android:layout_marginStart="@dimen/qqs_media_spacing" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" app:layout_constrainedWidth="true" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/icon" app:layout_constraintBottom_toTopOf="@id/header_artist" app:layout_constraintStart_toEndOf="@id/album_art" - app:layout_constraintEnd_toStartOf="@id/media_action_barrier" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> <!-- Artist name --> @@ -102,12 +107,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="3dp" - android:layout_marginBottom="24dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:layout_marginBottom="@dimen/qqs_media_spacing" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/header_title" app:layout_constraintStart_toStartOf="@id/header_title" - app:layout_constraintEnd_toStartOf="@id/media_action_barrier" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> <!-- Seek Bar --> @@ -140,15 +145,15 @@ android:id="@+id/action0" android:layout_width="48dp" android:layout_height="48dp" - android:layout_marginStart="4dp" + android:layout_marginStart="@dimen/qqs_media_spacing" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" android:visibility="gone" app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/header_artist" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/action1" - app:layout_constraintHorizontal_bias="1" + app:layout_constraintHorizontal_bias="0" > </Constraint> @@ -158,8 +163,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action0" app:layout_constraintRight_toLeftOf="@id/action2" > @@ -171,8 +177,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action3" > @@ -184,8 +191,9 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" - android:layout_marginTop="18dp" - app:layout_constraintTop_toBottomOf="@id/app_name" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action2" app:layout_constraintRight_toLeftOf="@id/action4" > @@ -196,11 +204,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:visibility="gone" - android:layout_marginTop="18dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintTop_toBottomOf="@id/app_name" + app:layout_constraintTop_toBottomOf="@id/header_artist" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintRight_toRightOf="parent" app:layout_constraintHorizontal_bias="0" diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml index d5a02c2d39d5..d89e0eb4df63 100644 --- a/packages/SystemUI/res/xml/media_expanded.xml +++ b/packages/SystemUI/res/xml/media_expanded.xml @@ -22,36 +22,39 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" android:layout_marginStart="18dp" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" - app:layout_constraintStart_toStartOf="parent" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@id/album_art" /> <Constraint android:id="@+id/app_name" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_center_guideline_padding" - android:layout_marginStart="10dp" - android:layout_marginTop="20dp" - app:layout_constraintTop_toTopOf="parent" + android:layout_marginStart="8dp" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/icon" - app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline" + app:layout_constraintEnd_toStartOf="@id/media_seamless" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constrainedWidth="true" app:layout_constraintHorizontal_bias="0" /> <Constraint android:id="@+id/media_seamless" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" + app:layout_constraintStart_toEndOf="@id/app_name" + app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintHorizontal_bias="1" app:layout_constrainedWidth="true" - app:layout_constraintWidth_min="60dp" - android:layout_marginTop="@dimen/qs_media_panel_outer_padding" - android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + app:layout_constraintWidth_min="48dp" + app:layout_constraintHeight_min="48dp" + android:layout_marginEnd="@dimen/qs_center_guideline_padding" android:layout_marginStart="@dimen/qs_center_guideline_padding" /> @@ -63,20 +66,21 @@ android:layout_marginStart="@dimen/qs_center_guideline_padding" android:alpha="0.5" android:visibility="gone" - app:layout_constraintTop_toTopOf="@id/app_name" - app:layout_constraintBottom_toBottomOf="@id/app_name" + app:layout_constraintHorizontal_bias="1" + app:layout_constraintTop_toTopOf="@id/icon" + app:layout_constraintBottom_toBottomOf="@id/icon" app:layout_constraintStart_toEndOf="@id/center_vertical_guideline" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="1" /> <Constraint android:id="@+id/album_art" android:layout_width="@dimen/qs_media_album_size" android:layout_height="@dimen/qs_media_album_size" - android:layout_marginTop="14dp" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" - app:layout_constraintTop_toBottomOf="@+id/app_name" + android:layout_marginBottom="@dimen/qqs_media_spacing" + app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" /> @@ -85,11 +89,11 @@ android:id="@+id/header_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qqs_media_spacing" + android:layout_marginStart="@dimen/qqs_media_spacing" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" - android:layout_marginTop="17dp" - android:layout_marginStart="16dp" app:layout_constrainedWidth="true" - app:layout_constraintTop_toBottomOf="@+id/app_name" + app:layout_constraintTop_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@id/album_art" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0"/> @@ -100,6 +104,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:layout_marginBottom="@dimen/qqs_media_spacing" android:layout_marginTop="3dp" app:layout_constrainedWidth="true" app:layout_constraintTop_toBottomOf="@id/header_title" @@ -112,7 +117,7 @@ android:id="@+id/media_progress_bar" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="3dp" + android:layout_marginTop="35dp" app:layout_constraintTop_toBottomOf="@id/header_artist" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -122,7 +127,7 @@ android:id="@+id/notification_media_progress_time" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="38dp" + android:layout_marginTop="70dp" android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:layout_marginStart="@dimen/qs_media_panel_outer_padding" app:layout_constraintTop_toBottomOf="@id/header_artist" @@ -135,13 +140,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginTop="5dp" - android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toLeftOf="@id/action1" - app:layout_constraintTop_toBottomOf="@id/notification_media_progress_time" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -155,6 +159,7 @@ app:layout_constraintLeft_toRightOf="@id/action0" app:layout_constraintRight_toLeftOf="@id/action2" app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -167,7 +172,7 @@ android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintLeft_toRightOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action3" - app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -177,10 +182,10 @@ android:layout_height="48dp" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintLeft_toRightOf="@id/action2" app:layout_constraintRight_toLeftOf="@id/action4" - app:layout_constraintTop_toTopOf="@id/action0" - android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> @@ -189,12 +194,12 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_marginStart="4dp" - android:layout_marginEnd="4dp" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toTopOf="@id/action0" + app:layout_constraintTop_toBottomOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent"> </Constraint> </ConstraintSet> diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java index 61951cc0b5e9..169a9c0c6eac 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java @@ -112,7 +112,7 @@ public final class PhoneStateMonitor { } else if (mLauncherShowing) { phoneState = getPhoneLauncherState(); } else { - phoneState = getPhoneAppState(); + phoneState = PHONE_STATE_APP_IMMERSIVE; } return phoneState; } @@ -161,16 +161,6 @@ public final class PhoneStateMonitor { } } - private int getPhoneAppState() { - if (isAppImmersive()) { - return PHONE_STATE_APP_IMMERSIVE; - } else if (isAppFullscreen()) { - return PHONE_STATE_APP_FULLSCREEN; - } else { - return PHONE_STATE_APP_DEFAULT; - } - } - private boolean isShadeFullscreen() { int statusBarState = mStatusBarStateController.getState(); return statusBarState == StatusBarState.KEYGUARD @@ -189,14 +179,6 @@ public final class PhoneStateMonitor { } } - private boolean isAppImmersive() { - return mStatusBarOptionalLazy.get().get().inImmersiveMode(); - } - - private boolean isAppFullscreen() { - return mStatusBarOptionalLazy.get().get().inFullscreenMode(); - } - private boolean isBouncerShowing() { return mStatusBarOptionalLazy.map( statusBarLazy -> statusBarLazy.get().isBouncerShowing()).orElse(false); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index e4f6d6cc6887..9b09cfd0dba6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -160,12 +160,12 @@ public class AuthBiometricFaceView extends AuthBiometricView { @Override protected void handleResetAfterError() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override protected void handleResetAfterHelp() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override @@ -185,7 +185,7 @@ public class AuthBiometricFaceView extends AuthBiometricView { if (newState == STATE_AUTHENTICATING_ANIMATING_IN || (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } // Do this last since the state variable gets updated. @@ -204,9 +204,8 @@ public class AuthBiometricFaceView extends AuthBiometricView { super.onAuthenticationFailed(failureReason); } - static void resetErrorView(Context context, TextView textView) { - textView.setTextColor(context.getResources().getColor( - R.color.biometric_dialog_gray, context.getTheme())); - textView.setVisibility(View.INVISIBLE); + private void resetErrorView() { + mIndicatorView.setTextColor(mTextColorHint); + mIndicatorView.setVisibility(View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java index 176e9e6b1c9b..45ee4ad9ae50 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java @@ -78,7 +78,7 @@ public class AuthBiometricFingerprintView extends AuthBiometricView { private void showTouchSensorString() { mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor); - mIndicatorView.setTextColor(R.color.biometric_dialog_gray); + mIndicatorView.setTextColor(mTextColorHint); } private void updateIcon(int lastState, int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index c748ab21b822..18206efefe9a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -83,7 +83,7 @@ public abstract class AuthBiometricView extends LinearLayout { * Authenticated, dialog animating away soon. */ protected static final int STATE_AUTHENTICATED = 6; - + @Retention(RetentionPolicy.SOURCE) @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP, STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED}) @@ -168,8 +168,8 @@ public abstract class AuthBiometricView extends LinearLayout { private final Injector mInjector; private final Handler mHandler; private final AccessibilityManager mAccessibilityManager; - private final int mTextColorError; - private final int mTextColorHint; + protected final int mTextColorError; + protected final int mTextColorHint; private AuthPanelController mPanelController; private PromptInfo mPromptInfo; @@ -183,7 +183,7 @@ public abstract class AuthBiometricView extends LinearLayout { private TextView mDescriptionView; private View mIconHolderView; protected ImageView mIconView; - @VisibleForTesting protected TextView mIndicatorView; + protected TextView mIndicatorView; // Negative button position, exclusively for the app-specified behavior @VisibleForTesting Button mNegativeButton; diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java new file mode 100644 index 000000000000..b77fcc822b88 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.flags; + +import android.annotation.NonNull; +import android.content.res.Resources; +import android.provider.DeviceConfig; +import android.util.SparseArray; + +import androidx.annotation.BoolRes; +import androidx.annotation.Nullable; + +import com.android.systemui.R; +import com.android.systemui.assist.DeviceConfigHelper; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.util.wrapper.BuildInfo; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; +/** + * Reads and caches feature flags for quick access + * + * Feature flags must be defined as boolean resources. For example: + * + * {@code + * <bool name="flag_foo_bar_baz">false</bool> + * } + * + * It is strongly recommended that the name of the resource begin with "flag_". + * + * Flags can be overridden via adb on development builds. For example, to override the flag from the + * previous example, do the following: + * + * {@code + * $ adb shell device_config put systemui flag_foo_bar_baz true + * } + * + * Note that all storage keys begin with "flag_", even if their associated resId does not. + * + * Calls to this class should probably be wrapped by {@link FeatureFlags}. + */ +@SysUISingleton +public class FeatureFlagReader { + private final Resources mResources; + private final DeviceConfigHelper mDeviceConfig; + private final boolean mAreFlagsOverrideable; + + private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>(); + + @Inject + public FeatureFlagReader( + @Main Resources resources, + BuildInfo build, + DeviceConfigHelper deviceConfig, + @Background Executor executor) { + mResources = resources; + mDeviceConfig = deviceConfig; + mAreFlagsOverrideable = + build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable); + + mDeviceConfig.addOnPropertiesChangedListener(executor, this::onPropertiesChanged); + } + + /** + * Returns true if the specified feature flag has been enabled. + * + * @param resId The backing boolean resource that determines the value of the flag. This value + * can be overridden via DeviceConfig on development builds. + */ + public boolean isEnabled(@BoolRes int resId) { + synchronized (mCachedFlags) { + CachedFlag cachedFlag = mCachedFlags.get(resId); + + if (cachedFlag == null) { + String name = resourceIdToFlagName(resId); + boolean value = mResources.getBoolean(resId); + if (mAreFlagsOverrideable) { + value = mDeviceConfig.getBoolean(flagNameToStorageKey(name), value); + } + + cachedFlag = new CachedFlag(name, value); + mCachedFlags.put(resId, cachedFlag); + } + + return cachedFlag.value; + } + } + + private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { + synchronized (mCachedFlags) { + for (String key : properties.getKeyset()) { + String flagName = storageKeyToFlagName(key); + if (flagName != null) { + clearCachedFlag(flagName); + } + } + } + } + + private void clearCachedFlag(String flagName) { + for (int i = 0; i < mCachedFlags.size(); i++) { + CachedFlag flag = mCachedFlags.valueAt(i); + if (flag.name.equals(flagName)) { + mCachedFlags.removeAt(i); + break; + } + } + } + + private String resourceIdToFlagName(@BoolRes int resId) { + String resName = mResources.getResourceEntryName(resId); + if (resName.startsWith(RESNAME_PREFIX)) { + resName = resName.substring(RESNAME_PREFIX.length()); + } + return resName; + } + + private String flagNameToStorageKey(String flagName) { + if (flagName.startsWith(STORAGE_KEY_PREFIX)) { + return flagName; + } else { + return STORAGE_KEY_PREFIX + flagName; + } + } + + @Nullable + private String storageKeyToFlagName(String configName) { + if (configName.startsWith(STORAGE_KEY_PREFIX)) { + return configName.substring(STORAGE_KEY_PREFIX.length()); + } else { + return null; + } + } + + private static class CachedFlag { + public final String name; + public final boolean value; + + private CachedFlag(String name, boolean value) { + this.name = name; + this.value = value; + } + } + + private static final String STORAGE_KEY_PREFIX = "flag_"; + private static final String RESNAME_PREFIX = "flag_"; +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index c18a6a45e286..743ac86cbdcb 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -29,6 +29,7 @@ import android.graphics.drawable.Icon; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.os.SystemProperties; import android.util.Log; import android.view.View; import android.view.ViewOutlineProvider; @@ -64,6 +65,11 @@ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; + private final boolean mShowAppName = SystemProperties.getBoolean( + "persist.sysui.qs_media_show_app_name", false); + private final boolean mShowDeviceName = SystemProperties.getBoolean( + "persist.sysui.qs_media_show_device_name", false); + private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); // Button IDs for QS controls @@ -265,6 +271,9 @@ public class MediaControlPanel { // App title TextView appName = mViewHolder.getAppName(); appName.setText(data.getApp()); + appName.setVisibility(mShowAppName ? View.VISIBLE : View.GONE); + setVisibleAndAlpha(collapsedSet, R.id.app_name, mShowAppName); + setVisibleAndAlpha(expandedSet, R.id.app_name, mShowAppName); // Artist name TextView artistText = mViewHolder.getArtistText(); @@ -277,6 +286,10 @@ public class MediaControlPanel { mViewHolder.getSeamless().setOnClickListener(v -> { mMediaOutputDialogFactory.create(data.getPackageName(), true); }); + TextView mDeviceName = mViewHolder.getSeamlessText(); + mDeviceName.setVisibility(mShowDeviceName ? View.VISIBLE : View.GONE); + setVisibleAndAlpha(collapsedSet, R.id.media_seamless_text, mShowDeviceName); + setVisibleAndAlpha(expandedSet, R.id.media_seamless_text, mShowDeviceName); ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index a23b07c5d685..34d1f6e1789c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -28,6 +28,7 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; +import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; @@ -37,6 +38,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; @@ -91,6 +93,7 @@ import android.view.Surface; import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -159,6 +162,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private static final String EXTRA_DISABLE_STATE = "disabled_state"; private static final String EXTRA_DISABLE2_STATE = "disabled2_state"; private static final String EXTRA_APPEARANCE = "appearance"; + private static final String EXTRA_BEHAVIOR = "behavior"; private static final String EXTRA_TRANSIENT_STATE = "transient_state"; /** Allow some time inbetween the long press for back and recents. */ @@ -209,9 +213,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mForceNavBarHandleOpaque; private boolean mIsCurrentUserSetup; - /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ + /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */ private @Appearance int mAppearance; + /** @see android.view.WindowInsetsController#setSystemBarsBehavior(int) */ + private @Behavior int mBehavior; + private boolean mTransientShown; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; private LightBarController mLightBarController; @@ -489,6 +496,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0); mDisabledFlags2 = savedState.getInt(EXTRA_DISABLE2_STATE, 0); mAppearance = savedState.getInt(EXTRA_APPEARANCE, 0); + mBehavior = savedState.getInt(EXTRA_BEHAVIOR, 0); mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false); } mSavedState = savedState; @@ -629,6 +637,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1); outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2); outState.putInt(EXTRA_APPEARANCE, mAppearance); + outState.putInt(EXTRA_BEHAVIOR, mBehavior); outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); if (mNavigationBarView != null) { mNavigationBarView.getLightTransitionsController().saveState(outState); @@ -889,8 +898,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } @@ -906,6 +916,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); } + if (mBehavior != behavior) { + mBehavior = behavior; + updateSystemUiStateFlags(-1); + } } @Override @@ -1319,6 +1333,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, + allowSystemGestureIgnoringBarVisibility()) .commitUpdate(mDisplayId); registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON); registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER); @@ -1421,6 +1437,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return mNavigationBarWindowState == WINDOW_STATE_SHOWING; } + private boolean allowSystemGestureIgnoringBarVisibility() { + return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; + } + /** * Checks current navigation bar mode and make transitions. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index bba8579c4f86..e9207f1feff3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -29,7 +29,6 @@ import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.qs.TouchAnimator.Listener; import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -50,9 +49,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; public static final float EXPANDED_TILE_DELAY = .86f; - private static final long QQS_FADE_IN_DURATION = 200L; - // Fade out faster than fade in to finish before QQS hides. - private static final long QQS_FADE_OUT_DURATION = 50L; + private final ArrayList<View> mAllViews = new ArrayList<>(); /** @@ -80,7 +77,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private TouchAnimator mBrightnessAnimator; private boolean mNeedsAnimatorUpdate = false; - private boolean mToShowing; private boolean mOnKeyguard; private boolean mAllowFancy; @@ -137,18 +133,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } } - void startAlphaAnimation(boolean show) { - if (show == mToShowing) { - return; - } - mToShowing = show; - if (show) { - CrossFadeHelper.fadeIn(mQs.getView(), QQS_FADE_IN_DURATION, 0 /* delay */); - } else { - CrossFadeHelper.fadeOut(mQs.getView(), QQS_FADE_OUT_DURATION, 0 /* delay */, - null /* endRunnable */); - } - } /** * Sets whether or not the keyguard is currently being shown with a collapsed header. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 562ac6414997..16e95900052f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -91,11 +91,6 @@ public class QSContainerImpl extends FrameLayout { setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); } - @Override - public boolean hasOverlappingRendering() { - return false; - } - void onMediaVisibilityChanged(boolean qsVisible) { mAnimateBottomOnNextLayout = qsVisible; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 9a0827d78e62..dbdd04a1e3ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -186,7 +186,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { boolean sizeChanged = (oldTop - oldBottom) != (top - bottom); if (sizeChanged) { - setQsExpansion(mLastQSExpansion, mLastHeaderTranslation); + setQsExpansion(mLastQSExpansion, mLastQSExpansion); } }); } @@ -391,9 +391,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public void setQsExpansion(float expansion, float headerTranslation) { if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); - if (mQSAnimator != null) { - mQSAnimator.startAlphaAnimation(headerTranslation == 0 /* show */); - } mContainer.setExpansion(expansion); final float translationScaleY = expansion - 1; boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5eba147ab279..9e7ed0f6e365 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -686,11 +686,12 @@ public class QSPanel extends LinearLayout implements Tunable { */ protected void updateMediaHostContentMargins(ViewGroup mediaHostView) { if (mUsingMediaPlayer) { - int marginStart = mContentMarginStart; + int marginStart = 0; + int marginEnd = 0; if (mUsingHorizontalLayout) { - marginStart = 0; + marginEnd = mContentMarginEnd; } - updateMargins(mediaHostView, marginStart, mContentMarginEnd); + updateMargins(mediaHostView, marginStart, marginEnd); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index db2750b8842f..9dce19192dbe 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -47,7 +47,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -79,13 +79,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final String mScreenshotId; private final boolean mSmartActionsEnabled; private final Random mRandom = new Random(); - private final Supplier<ShareTransition> mSharedElementTransition; + private final Supplier<ActionTransition> mSharedElementTransition; private final ImageExporter mImageExporter; SaveImageInBackgroundTask(Context context, ImageExporter exporter, ScreenshotSmartActions screenshotSmartActions, ScreenshotController.SaveImageInBackgroundData data, - Supplier<ShareTransition> sharedElementTransition) { + Supplier<ActionTransition> sharedElementTransition) { mContext = context; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); @@ -150,7 +150,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageData.uri = uri; mImageData.smartActions = smartActions; mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); - mImageData.editAction = createEditAction(mContext, mContext.getResources(), uri); + mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); mParams.mActionsReadyListener.onActionsReady(mImageData); @@ -204,9 +204,9 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { * Assumes that the action intent is sent immediately after being supplied. */ @VisibleForTesting - Supplier<ShareTransition> createShareAction(Context context, Resources r, Uri uri) { + Supplier<ActionTransition> createShareAction(Context context, Resources r, Uri uri) { return () -> { - ShareTransition transition = mSharedElementTransition.get(); + ActionTransition transition = mSharedElementTransition.get(); // Note: Both the share and edit actions are proxied through ActionProxyReceiver in // order to do some common work like dismissing the keyguard and sending @@ -259,52 +259,57 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Icon.createWithResource(r, R.drawable.ic_screenshot_share), r.getString(com.android.internal.R.string.share), shareAction); - transition.shareAction = shareActionBuilder.build(); + transition.action = shareActionBuilder.build(); return transition; }; } @VisibleForTesting - Notification.Action createEditAction(Context context, Resources r, Uri uri) { - // Note: Both the share and edit actions are proxied through ActionProxyReceiver in - // order to do some common work like dismissing the keyguard and sending - // closeSystemWindows - - // Create an edit intent, if a specific package is provided as the editor, then - // launch that directly - String editorPackage = context.getString(R.string.config_screenshotEditor); - Intent editIntent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - editIntent.setDataAndType(uri, "image/png"); - editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + Supplier<ActionTransition> createEditAction(Context context, Resources r, Uri uri) { + return () -> { + ActionTransition transition = mSharedElementTransition.get(); + // Note: Both the share and edit actions are proxied through ActionProxyReceiver in + // order to do some common work like dismissing the keyguard and sending + // closeSystemWindows - PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0, - editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); + // Create an edit intent, if a specific package is provided as the editor, then + // launch that directly + String editorPackage = context.getString(R.string.config_screenshotEditor); + Intent editIntent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + editIntent.setDataAndType(uri, "image/png"); + editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - // Make sure pending intents for the system user are still unique across users - // by setting the (otherwise unused) request code to the current user id. - int requestCode = mContext.getUserId(); + PendingIntent pendingIntent = PendingIntent.getActivityAsUser( + context, 0, editIntent, PendingIntent.FLAG_IMMUTABLE, + transition.bundle, UserHandle.CURRENT); - // Create a edit action - PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, - new Intent(context, ActionProxyReceiver.class) - .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) - .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) - .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, - mSmartActionsEnabled) - .setAction(Intent.ACTION_EDIT) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, - UserHandle.SYSTEM); - Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( - Icon.createWithResource(r, R.drawable.ic_screenshot_edit), - r.getString(com.android.internal.R.string.screenshot_edit), editAction); + // Make sure pending intents for the system user are still unique across users + // by setting the (otherwise unused) request code to the current user id. + int requestCode = mContext.getUserId(); + + // Create a edit action + PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, + new Intent(context, ActionProxyReceiver.class) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) + .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) + .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled) + .setAction(Intent.ACTION_EDIT) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( + Icon.createWithResource(r, R.drawable.ic_screenshot_edit), + r.getString(com.android.internal.R.string.screenshot_edit), editAction); - return editActionBuilder.build(); + transition.action = editActionBuilder.build(); + return transition; + }; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 7d5779949074..d77d1ea75f30 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -81,7 +81,7 @@ import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; @@ -115,17 +115,17 @@ public class ScreenshotController { */ static class SavedImageData { public Uri uri; - public Supplier<ShareTransition> shareTransition; - public Notification.Action editAction; + public Supplier<ActionTransition> shareTransition; + public Supplier<ActionTransition> editTransition; public Notification.Action deleteAction; public List<Notification.Action> smartActions; /** - * POD for shared element transition to share sheet. + * POD for shared element transition. */ - static class ShareTransition { + static class ActionTransition { public Bundle bundle; - public Notification.Action shareAction; + public Notification.Action action; public Runnable onCancelRunnable; } @@ -135,7 +135,7 @@ public class ScreenshotController { public void reset() { uri = null; shareTransition = null; - editAction = null; + editTransition = null; deleteAction = null; smartActions = null; } @@ -352,6 +352,10 @@ public class ScreenshotController { } } + boolean isPendingSharedTransition() { + return mScreenshotView.isPendingSharedTransition(); + } + /** * Release the constructed window context. */ @@ -626,7 +630,7 @@ public class ScreenshotController { } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, - mScreenshotSmartActions, data, getShareTransitionSupplier()); + mScreenshotSmartActions, data, getActionTransitionSupplier()); mSaveInBgTask.execute(); } @@ -680,7 +684,7 @@ public class ScreenshotController { * Supplies the necessary bits for the shared element transition to share sheet. * Note that once supplied, the action intent to share must be sent immediately after. */ - private Supplier<ShareTransition> getShareTransitionSupplier() { + private Supplier<ActionTransition> getActionTransitionSupplier() { return () -> { ExitTransitionCallbacks cb = new ExitTransitionCallbacks() { @Override @@ -689,7 +693,13 @@ public class ScreenshotController { } @Override - public void onFinish() { } + public void hideSharedElements() { + resetScreenshotView(); + } + + @Override + public void onFinish() { + } }; Pair<ActivityOptions, ExitTransitionCoordinator> transition = @@ -698,7 +708,7 @@ public class ScreenshotController { ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); - ShareTransition supply = new ShareTransition(); + ActionTransition supply = new ActionTransition(); supply.bundle = transition.first.toBundle(); supply.onCancelRunnable = () -> ActivityOptions.stopSharedElementAnimation(mWindow); return supply; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 41c2098d2505..211f5072bd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -74,7 +74,7 @@ import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; @@ -106,7 +106,6 @@ public class ScreenshotView extends FrameLayout implements private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350; private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183; private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade - private static final long SCREENSHOT_DISMISS_SHARE_OFFSET_MS = 300; // delay after share clicked private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f; private static final float ROUNDED_CORNER_RADIUS = .05f; private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe @@ -142,7 +141,7 @@ public class ScreenshotView extends FrameLayout implements private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; private Animator mDismissAnimation; - private boolean mIgnoreDismiss; + private boolean mPendingSharedTransition; private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>(); private PendingInteraction mPendingInteraction; @@ -296,6 +295,10 @@ public class ScreenshotView extends FrameLayout implements requestFocus(); } + View getScreenshotPreview() { + return mScreenshotPreview; + } + /** * Set up the logger and callback on dismissal. * @@ -535,44 +538,22 @@ public class ScreenshotView extends FrameLayout implements }); return animator; } - protected View getScreenshotPreview() { - return mScreenshotPreview; - } void setChipIntents(ScreenshotController.SavedImageData imageData) { mShareChip.setOnClickListener(v -> { - ShareTransition transition = imageData.shareTransition.get(); - try { - mIgnoreDismiss = true; - transition.shareAction.actionIntent.send(); - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); - - // Ensures that we delay dismissing until transition has started. - postDelayed(() -> { - mIgnoreDismiss = false; - animateDismissal(); - }, SCREENSHOT_DISMISS_SHARE_OFFSET_MS); - } catch (PendingIntent.CanceledException e) { - mIgnoreDismiss = false; - if (transition.onCancelRunnable != null) { - transition.onCancelRunnable.run(); - } - Log.e(TAG, "Share intent cancelled", e); - } + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); + startSharedTransition( + imageData.shareTransition.get()); + }); + mEditChip.setOnClickListener(v -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); + startSharedTransition( + imageData.editTransition.get()); }); - mEditChip.setPendingIntent(imageData.editAction.actionIntent, - () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); - animateDismissal(); - }); mScreenshotPreview.setOnClickListener(v -> { - try { - imageData.editAction.actionIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "PendingIntent was cancelled", e); - } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); - animateDismissal(); + startSharedTransition( + imageData.editTransition.get()); }); if (mPendingInteraction != null) { @@ -611,14 +592,15 @@ public class ScreenshotView extends FrameLayout implements return (mDismissAnimation != null && mDismissAnimation.isRunning()); } + boolean isPendingSharedTransition() { + return mPendingSharedTransition; + } + void animateDismissal() { - animateDismissal(createScreenshotDismissAnimation()); + animateDismissal(createScreenshotTranslateDismissAnimation()); } private void animateDismissal(Animator dismissAnimation) { - if (mIgnoreDismiss) { - return; - } if (DEBUG_WINDOW) { Log.d(TAG, "removing OnComputeInternalInsetsListener"); } @@ -671,6 +653,7 @@ public class ScreenshotView extends FrameLayout implements getViewTreeObserver().removeOnComputeInternalInsetsListener(this); // Clear any references to the bitmap mScreenshotPreview.setImageDrawable(null); + mPendingSharedTransition = false; mActionsContainerBackground.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setAlpha(0f); @@ -698,7 +681,23 @@ public class ScreenshotView extends FrameLayout implements mScreenshotSelectorView.stop(); } - private AnimatorSet createScreenshotDismissAnimation() { + private void startSharedTransition(ActionTransition transition) { + try { + mPendingSharedTransition = true; + transition.action.actionIntent.send(); + + // fade out non-preview UI + createScreenshotFadeDismissAnimation().start(); + } catch (PendingIntent.CanceledException e) { + mPendingSharedTransition = false; + if (transition.onCancelRunnable != null) { + transition.onCancelRunnable.run(); + } + Log.e(TAG, "Intent cancelled", e); + } + } + + private AnimatorSet createScreenshotTranslateDismissAnimation() { ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS); alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS); @@ -725,6 +724,19 @@ public class ScreenshotView extends FrameLayout implements return animSet; } + private ValueAnimator createScreenshotFadeDismissAnimation() { + ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); + alphaAnim.addUpdateListener(animation -> { + float alpha = 1 - animation.getAnimatedFraction(); + mDismissButton.setAlpha(alpha); + mActionsContainerBackground.setAlpha(alpha); + mActionsContainer.setAlpha(alpha); + mBackgroundProtection.setAlpha(alpha); + }); + alphaAnim.setDuration(600); + return alphaAnim; + } + /** * Create a drawable using the size of the bitmap and insets as the fractional inset parameters. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index c33bbc51ed5b..144ad39a32aa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -72,7 +72,9 @@ public class TakeScreenshotService extends Service { if (DEBUG_DISMISS) { Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); } - mScreenshot.dismissScreenshot(false); + if (!mScreenshot.isPendingSharedTransition()) { + mScreenshot.dismissScreenshot(false); + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index fbb80428d68c..c4fa6df56775 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -34,7 +34,6 @@ import android.app.StatusBarManager.WindowType; import android.app.StatusBarManager.WindowVisibleState; import android.content.ComponentName; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; @@ -50,6 +49,7 @@ import android.util.Pair; import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import androidx.annotation.NonNull; @@ -90,7 +90,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT; private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; - private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT; + private static final int MSG_SYSTEM_BAR_CHANGED = 6 << MSG_SHIFT; private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; @@ -131,17 +131,16 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT; private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT; private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT; - private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT; - private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT; - private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT; - private static final int MSG_SHOW_TOAST = 53 << MSG_SHIFT; - private static final int MSG_HIDE_TOAST = 54 << MSG_SHIFT; - private static final int MSG_TRACING_STATE_CHANGED = 55 << MSG_SHIFT; - private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT; - private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT; - private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT; + private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 50 << MSG_SHIFT; + private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT; + private static final int MSG_SHOW_TOAST = 52 << MSG_SHIFT; + private static final int MSG_HIDE_TOAST = 53 << MSG_SHIFT; + private static final int MSG_TRACING_STATE_CHANGED = 54 << MSG_SHIFT; + private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 55 << MSG_SHIFT; + private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 56 << MSG_SHIFT; + private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT; //TODO(b/169175022) Update name and when feature name is locked. - private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 59 << MSG_SHIFT; + private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 58 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -308,10 +307,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void onRecentsAnimationStateChanged(boolean running) { } /** - * @see IStatusBar#onSystemBarAppearanceChanged(int, int, AppearanceRegion[], boolean). + * @see IStatusBar#onSystemBarAttributesChanged. */ - default void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { } + default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { } /** * @see IStatusBar#showTransient(int, int[]). @@ -324,12 +324,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void abortTransient(int displayId, @InternalInsetsType int[] types) { } /** - * @see IStatusBar#topAppWindowChanged(int, boolean, boolean). - */ - default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - } - - /** * Called to notify System UI that a warning about the device going to sleep * due to prolonged user inactivity should be shown. */ @@ -547,18 +541,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - synchronized (mLock) { - SomeArgs args = SomeArgs.obtain(); - args.argi1 = displayId; - args.argi2 = isFullscreen ? 1 : 0; - args.argi3 = isImmersive ? 1 : 0; - mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, args).sendToTarget(); - } - - } - - @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher, boolean isMultiClientImeEnabled) { synchronized (mLock) { @@ -969,15 +951,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.argi1 = displayId; args.argi2 = appearance; args.argi3 = navbarColorManagedByIme ? 1 : 0; args.arg1 = appearanceRegions; - mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget(); + args.argi4 = behavior; + args.argi5 = isFullscreen ? 1 : 0; + mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget(); } } @@ -1328,11 +1313,12 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0); } break; - case MSG_SYSTEM_BAR_APPEARANCE_CHANGED: + case MSG_SYSTEM_BAR_CHANGED: args = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).onSystemBarAppearanceChanged(args.argi1, args.argi2, - (AppearanceRegion[]) args.arg1, args.argi3 == 1); + mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2, + (AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4, + args.argi5 == 1); } args.recycle(); break; @@ -1352,15 +1338,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } break; } - case MSG_TOP_APP_WINDOW_CHANGED: { - args = (SomeArgs) msg.obj; - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).topAppWindowChanged( - args.argi1, args.argi2 != 0, args.argi3 != 0); - } - args.recycle(); - break; - } case MSG_SHOW_INATTENTIVE_SLEEP_WARNING: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showInattentiveSleepWarning(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index bbc4b780bc52..964c499e4b42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -16,78 +16,36 @@ package com.android.systemui.statusbar; -import android.annotation.NonNull; -import android.provider.DeviceConfig; -import android.util.ArrayMap; - +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Background; - -import java.util.Map; -import java.util.concurrent.Executor; +import com.android.systemui.flags.FeatureFlagReader; import javax.inject.Inject; /** * Class to manage simple DeviceConfig-based feature flags. * - * To enable or disable a flag, run: - * - * {@code - * $ adb shell device_config put systemui <key> <true|false> -* } - * - * You will probably need to restart systemui for the changes to be picked up: - * - * {@code - * $ adb shell am restart com.android.systemui - * } + * See {@link FeatureFlagReader} for instructions on defining and flipping flags. */ @SysUISingleton public class FeatureFlags { - private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); + private final FeatureFlagReader mFlagReader; @Inject - public FeatureFlags(@Background Executor executor) { - DeviceConfig.addOnPropertiesChangedListener( - /* namespace= */ "systemui", - executor, - this::onPropertiesChanged); + public FeatureFlags(FeatureFlagReader flagReader) { + mFlagReader = flagReader; } public boolean isNewNotifPipelineEnabled() { - return getDeviceConfigFlag("notification.newpipeline.enabled", /* defaultValue= */ true); + return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2); } public boolean isNewNotifPipelineRenderingEnabled() { - return isNewNotifPipelineEnabled() - && getDeviceConfigFlag("notification.newpipeline.rendering", /* defaultValue= */ - false); + return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering); } - /** - * Flag used for guarding development of b/171917882. - */ + /** b/171917882 */ public boolean isTwoColumnNotificationShadeEnabled() { - return getDeviceConfigFlag("notification.twocolumn", /* defaultValue= */ false); - } - - private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - synchronized (mCachedDeviceConfigFlags) { - for (String key : properties.getKeyset()) { - mCachedDeviceConfigFlags.remove(key); - } - } - } - - private boolean getDeviceConfigFlag(String key, boolean defaultValue) { - synchronized (mCachedDeviceConfigFlags) { - Boolean flag = mCachedDeviceConfigFlags.get(key); - if (flag == null) { - flag = DeviceConfig.getBoolean(/* namespace= */ "systemui", key, defaultValue); - mCachedDeviceConfigFlags.put(key, flag); - } - return flag; - } + return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java index e9442499a8ce..6ba52156c374 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java @@ -88,11 +88,6 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll private boolean mIsFullscreen = false; /** - * If the navigation bar can stay hidden when the display gets tapped. - */ - private boolean mIsImmersive = false; - - /** * If the device is currently pulsing (AOD2). */ private boolean mPulsing; @@ -360,13 +355,12 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll } @Override - public void setFullscreenState(boolean isFullscreen, boolean isImmersive) { - if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) { + public void setFullscreenState(boolean isFullscreen) { + if (mIsFullscreen != isFullscreen) { mIsFullscreen = isFullscreen; - mIsImmersive = isImmersive; synchronized (mListeners) { for (RankedListener rl : new ArrayList<>(mListeners)) { - rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive); + rl.mListener.onFullscreenStateChanged(isFullscreen, true /* isImmersive */); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java index 9f8fe35dfbc9..a2e07b289e9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java @@ -121,7 +121,7 @@ public interface SysuiStatusBarStateController extends StatusBarStateController /** * Set the fullscreen state */ - void setFullscreenState(boolean isFullscreen, boolean isImmersive); + void setFullscreenState(boolean isFullscreen); /** * Set pulsing diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java index d27a3d53c0a2..7d134057ee76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java @@ -22,7 +22,8 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; import android.view.View; -import android.view.WindowInsetsController; +import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; @@ -52,7 +53,7 @@ public class LightsOutNotifController { private final WindowManager mWindowManager; /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ - @VisibleForTesting @WindowInsetsController.Appearance int mAppearance; + @VisibleForTesting @Appearance int mAppearance; private int mDisplayId; private View mLightsOutNotifView; @@ -146,10 +147,9 @@ public class LightsOutNotifController { private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() { @Override - public void onSystemBarAppearanceChanged(int displayId, - @WindowInsetsController.Appearance int appearance, - AppearanceRegion[] appearanceRegions, - boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 8f2382486abd..8ea173bfdc58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -112,6 +112,7 @@ import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; @@ -444,9 +445,6 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mTransientShown; - private boolean mAppFullscreen; - private boolean mAppImmersive; - private final DisplayMetrics mDisplayMetrics; // XXX: gesture research @@ -574,6 +572,8 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationEntry mDraggedDownEntry; private boolean mLaunchCameraWhenFinishedWaking; private boolean mLaunchCameraOnFinishedGoingToSleep; + private boolean mLaunchEmergencyActionWhenFinishedWaking; + private boolean mLaunchEmergencyActionOnFinishedGoingToSleep; private int mLastCameraLaunchSource; protected PowerManager.WakeLock mGestureWakeLock; private Vibrator mVibrator; @@ -921,10 +921,8 @@ public class StatusBar extends SystemUI implements DemoMode, if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) { showTransientUnchecked(); } - onSystemBarAppearanceChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, - result.mNavbarColorManagedByIme); - mAppFullscreen = result.mAppFullscreen; - mAppImmersive = result.mAppImmersive; + onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions, + result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, @@ -2345,8 +2343,9 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { if (displayId != mDisplayId) { return; } @@ -2359,6 +2358,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarMode, navbarColorManagedByIme); updateBubblesVisibility(); + mStatusBarStateController.setFullscreenState(isFullscreen); } @Override @@ -2432,16 +2432,6 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - if (displayId != mDisplayId) { - return; - } - mAppFullscreen = isFullscreen; - mAppImmersive = isImmersive; - mStatusBarStateController.setFullscreenState(isFullscreen, isImmersive); - } - - @Override public void showWirelessChargingAnimation(int batteryLevel) { showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0); } @@ -2551,16 +2541,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - /** Returns whether the top activity is in fullscreen mode. */ - public boolean inFullscreenMode() { - return mAppFullscreen; - } - - /** Returns whether the top activity is in immersive mode. */ - public boolean inImmersiveMode() { - return mAppImmersive; - } - public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; @@ -3844,6 +3824,14 @@ public class StatusBar extends SystemUI implements DemoMode, // is correct. mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource)); } + + if (mLaunchEmergencyActionOnFinishedGoingToSleep) { + mLaunchEmergencyActionOnFinishedGoingToSleep = false; + + // This gets executed before we will show Keyguard, so post it in order that the + // state is correct. + mHandler.post(() -> onEmergencyActionLaunchGestureDetected()); + } updateIsKeyguard(); } @@ -3890,6 +3878,13 @@ public class StatusBar extends SystemUI implements DemoMode, false /* animate */, mLastCameraLaunchSource); mLaunchCameraWhenFinishedWaking = false; } + if (mLaunchEmergencyActionWhenFinishedWaking) { + mLaunchEmergencyActionWhenFinishedWaking = false; + Intent emergencyIntent = getEmergencyActionIntent(); + if (emergencyIntent != null) { + mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT); + } + } updateScrimController(); } }; @@ -4027,20 +4022,65 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onEmergencyActionLaunchGestureDetected() { - // TODO (b/169793384) Polish the panic gesture to be just like its older brother, camera. + Intent emergencyIntent = getEmergencyActionIntent(); + + if (emergencyIntent == null) { + Log.wtf(TAG, "Couldn't find an app to process the emergency intent."); + return; + } + + if (isGoingToSleep()) { + mLaunchEmergencyActionOnFinishedGoingToSleep = true; + return; + } + + if (!mDeviceInteractive) { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_GESTURE, + "com.android.systemui:EMERGENCY_GESTURE"); + } + // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for + // app-side haptic experimentation. + + if (!mStatusBarKeyguardViewManager.isShowing()) { + startActivityDismissingKeyguard(emergencyIntent, + false /* onlyProvisioned */, true /* dismissShade */, + true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0); + return; + } + + if (!mDeviceInteractive) { + // Avoid flickering of the scrim when we instant launch the camera and the bouncer + // comes on. + mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L); + } + + if (isWakingUpOrAwake()) { + if (mStatusBarKeyguardViewManager.isBouncerShowing()) { + mStatusBarKeyguardViewManager.reset(true /* hide */); + } + mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT); + return; + } + // We need to defer the emergency action launch until the screen comes on, since otherwise + // we will dismiss us too early since we are waiting on an activity to be drawn and + // incorrectly get notified because of the screen on event (which resumes and pauses + // some activities) + mLaunchEmergencyActionWhenFinishedWaking = true; + } + + private @Nullable Intent getEmergencyActionIntent() { Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY); PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0); if (resolveInfo == null) { - // TODO(b/171084088) Upgrade log to wtf when we have default app in main branch. - Log.d(TAG, "Couldn't find an app to process the emergency intent."); - return; + Log.wtf(TAG, "Couldn't find an app to process the emergency intent."); + return null; } - emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(emergencyIntent, /*dismissShade=*/true); + return emergencyIntent; } boolean isCameraAllowedByAdmin() { @@ -4100,8 +4140,10 @@ public class StatusBar extends SystemUI implements DemoMode, ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming() ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER; mScrimController.transitionTo(state); - } else if (isInLaunchTransition() || mLaunchCameraWhenFinishedWaking + } else if (isInLaunchTransition() + || mLaunchCameraWhenFinishedWaking || launchingAffordanceWithPreview) { + // TODO(b/170133395) Investigate whether Emergency Gesture flag should be included here. mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } else if (mBrightnessMirrorVisible) { mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR); diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java b/packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java new file mode 100644 index 000000000000..5e68a15f8ec7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.wrapper; + +import android.os.Build; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Testable wrapper around {@link Build}. + */ +@Singleton +public class BuildInfo { + @Inject + public BuildInfo() { + } + + /** @see Build#IS_DEBUGGABLE */ + public boolean isDebuggable() { + return Build.IS_DEBUGGABLE; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java new file mode 100644 index 000000000000..c79037b761aa --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.flags; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.provider.DeviceConfig; + +import androidx.annotation.BoolRes; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.assist.DeviceConfigHelper; +import com.android.systemui.util.wrapper.BuildInfo; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +@SmallTest +public class FeatureFlagReaderTest extends SysuiTestCase { + @Mock private Resources mResources; + @Mock private BuildInfo mBuildInfo; + @Mock private DeviceConfigHelper mDeviceConfig; + @Mock private Executor mBackgroundExecutor; + + private FeatureFlagReader mReader; + + @Captor private ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> mListenerCaptor; + private DeviceConfig.OnPropertiesChangedListener mPropChangeListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mDeviceConfig.getBoolean(anyString(), anyBoolean())) + .thenAnswer(invocation -> invocation.getArgument(1)); + + defineFlag(FLAG_RESID_0, false); + defineFlag(FLAG_RESID_1, true); + + initialize(true, true); + + verify(mDeviceConfig).addOnPropertiesChangedListener(any(), mListenerCaptor.capture()); + mPropChangeListener = mListenerCaptor.getValue(); + } + + private void initialize(boolean isDebuggable, boolean isOverrideable) { + when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable); + when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable); + mReader = new FeatureFlagReader(mResources, mBuildInfo, mDeviceConfig, mBackgroundExecutor); + } + + @Test + public void testCantOverrideIfNotDebuggable() { + // GIVEN that the build is not debuggable + initialize(false, true); + + // GIVEN that a flag has been overridden to true + overrideFlag(FLAG_RESID_0, true); + + // THEN the flag is still false + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + } + + @Test + public void testCantOverrideIfNotOverrideable() { + // GIVEN that flags are not overrideable + initialize(true, false); + + // GIVEN that a flag has been overridden to true + overrideFlag(FLAG_RESID_0, true); + + // THEN the flag is still false + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + } + + @Test + public void testReadFlags() { + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + assertTrue(mReader.isEnabled(FLAG_RESID_1)); + } + + @Test + public void testOverrideFlags() { + // GIVEN that flags are overridden + overrideFlag(FLAG_RESID_0, true); + overrideFlag(FLAG_RESID_1, false); + + // THEN the reader returns the overridden values + assertTrue(mReader.isEnabled(FLAG_RESID_0)); + assertFalse(mReader.isEnabled(FLAG_RESID_1)); + } + + @Test + public void testThatFlagReadsAreCached() { + // GIVEN that a flag is overridden + overrideFlag(FLAG_RESID_0, true); + + // WHEN the flag is queried many times + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + mReader.isEnabled(FLAG_RESID_0); + + // THEN the underlying resource and override are only queried once + verify(mResources, times(1)).getBoolean(FLAG_RESID_0); + verify(mDeviceConfig, times(1)).getBoolean(fakeStorageKey(FLAG_RESID_0), false); + } + + @Test + public void testCachesAreClearedAfterPropsChange() { + // GIVEN a flag whose value has already been queried + assertFalse(mReader.isEnabled(FLAG_RESID_0)); + + // WHEN the value of the flag changes + overrideFlag(FLAG_RESID_0, true); + Map<String, String> changedMap = new HashMap<>(); + changedMap.put(fakeStorageKey(FLAG_RESID_0), "true"); + DeviceConfig.Properties properties = + new DeviceConfig.Properties("systemui", changedMap); + mPropChangeListener.onPropertiesChanged(properties); + + // THEN the new value is provided + assertTrue(mReader.isEnabled(FLAG_RESID_0)); + } + + private void defineFlag(int resId, boolean value) { + when(mResources.getBoolean(resId)).thenReturn(value); + when(mResources.getResourceEntryName(resId)).thenReturn(fakeStorageKey(resId)); + } + + private void overrideFlag(int resId, boolean value) { + when(mDeviceConfig.getBoolean(eq(fakeStorageKey(resId)), anyBoolean())) + .thenReturn(value); + } + + private String fakeStorageKey(@BoolRes int resId) { + return "flag_testname_" + resId; + } + + private static final int FLAG_RESID_0 = 47; + private static final int FLAG_RESID_1 = 48; +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index ced8428e6e6b..03f93fa12451 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -43,7 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import org.junit.Before; import org.junit.Test; @@ -177,10 +177,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")).get().shareAction; + Uri.parse("Screenshot_123.png")).get().action; Intent intent = shareAction.actionIntent.getIntent(); assertNotNull(intent); @@ -205,10 +205,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")); + Uri.parse("Screenshot_123.png")).get().action; Intent intent = editAction.actionIntent.getIntent(); assertNotNull(intent); @@ -233,7 +233,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action deleteAction = task.createDeleteAction(mContext, mContext.getResources(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index d2d57087485c..2917dfafd6a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.eq; @@ -30,6 +31,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.os.Bundle; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import androidx.test.filters.SmallTest; @@ -116,24 +118,27 @@ public class CommandQueueTest extends SysuiTestCase { } @Test - public void testOnSystemBarAppearanceChanged() { - doTestOnSystemBarAppearanceChanged(DEFAULT_DISPLAY, 1, - new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false); + public void testOnSystemBarAttributesChanged() { + doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1, + new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false, + BEHAVIOR_DEFAULT, false); } @Test - public void testOnSystemBarAppearanceChangedForSecondaryDisplay() { - doTestOnSystemBarAppearanceChanged(SECONDARY_DISPLAY, 1, - new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false); + public void testOnSystemBarAttributesChangedForSecondaryDisplay() { + doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1, + new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false, + BEHAVIOR_DEFAULT, false); } - private void doTestOnSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - mCommandQueue.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions, - navbarColorManagedByIme); + private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { + mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); waitForIdleSync(); - verify(mCallbacks).onSystemBarAppearanceChanged(eq(displayId), eq(appearance), - eq(appearanceRegions), eq(navbarColorManagedByIme)); + verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance), + eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java index dbb451277535..cdfab1eec609 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; +import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -95,21 +96,25 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { @Test public void testAreLightsOut_lightsOut() { - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); assertTrue(mLightsOutNotifController.areLightsOut()); } @Test public void testAreLightsOut_lightsOn() { - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_ON /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); assertFalse(mLightsOutNotifController.areLightsOut()); } @@ -128,16 +133,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOut_withNotifs_onSystemBarAppearanceChanged() { + public void testLightsOut_withNotifs_onSystemBarAttributesChanged() { // GIVEN active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(true); // WHEN lights out - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we should show dot assertTrue(mLightsOutNotifController.shouldShowDot()); @@ -145,16 +152,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOut_withoutNotifs_onSystemBarAppearanceChanged() { + public void testLightsOut_withoutNotifs_onSystemBarAttributesChanged() { // GIVEN no active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(false); // WHEN lights out - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_OUT /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we shouldn't show the dot assertFalse(mLightsOutNotifController.shouldShowDot()); @@ -162,16 +171,18 @@ public class LightsOutNotifControllerTest extends SysuiTestCase { } @Test - public void testLightsOn_afterLightsOut_onSystemBarAppearanceChanged() { + public void testLightsOn_afterLightsOut_onSystemBarAttributesChanged() { // GIVEN active visible notifications when(mEntryManager.hasActiveNotifications()).thenReturn(true); // WHEN lights on - mCallbacks.onSystemBarAppearanceChanged( + mCallbacks.onSystemBarAttributesChanged( mDisplayId /* display id */, LIGHTS_ON /* appearance */, null /* appearanceRegions */, - false /* navbarColorManagedByIme */); + false /* navbarColorManagedByIme */, + BEHAVIOR_DEFAULT, + false /* isFullscreen */); // THEN we shouldn't show the dot assertFalse(mLightsOutNotifController.shouldShowDot()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 8e84f1a4e843..51ce8e59e999 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -24,7 +24,6 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.fail; -import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -35,7 +34,6 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -45,7 +43,6 @@ import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; -import android.content.Intent; import android.content.IntentFilter; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.fingerprint.FingerprintManager; @@ -87,7 +84,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; @@ -151,10 +147,8 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -887,19 +881,6 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } - @Ignore // TODO (b/175240607) - Figure out if the device will actually dial 911. - @Test - public void onEmergencyActionLaunchGesture_launchesEmergencyIntent() { - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - StatusBar statusBarSpy = spy(mStatusBar); - - statusBarSpy.onEmergencyActionLaunchGestureDetected(); - - verify(statusBarSpy).startActivity(intentCaptor.capture(), eq(true)); - Intent sentIntent = intentCaptor.getValue(); - assertEquals(sentIntent.getAction(), EmergencyGesture.ACTION_LAUNCH_EMERGENCY); - } - public static class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java index 3357be8c8b84..fe01f841aa16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java @@ -19,6 +19,7 @@ package com.android.systemui.util.wakelock; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.os.Build; import android.os.PowerManager; import androidx.test.filters.SmallTest; @@ -85,4 +86,14 @@ public class WakeLockTest extends SysuiTestCase { assertTrue(ran[0]); assertFalse(mInner.isHeld()); } + + @Test + public void prodBuild_wakeLock_releaseWithoutAcquire_noThrow() { + if (Build.IS_ENG) { + return; + } + + // shouldn't throw an exception on production builds + mWakeLock.release(WHY); + } }
\ No newline at end of file diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/services/accessibility/OWNERS +++ b/services/accessibility/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java new file mode 100644 index 000000000000..9c908c386efd --- /dev/null +++ b/services/core/java/android/power/PowerStatsInternal.java @@ -0,0 +1,42 @@ +/* + * 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 android.power; + +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; + +import java.util.concurrent.CompletableFuture; + +/** + * Power stats local system service interface. + * + * @hide Only for use within Android OS. + */ +public abstract class PowerStatsInternal { + /** + * Returns a CompletableFuture that will get an {@link EnergyConsumerResult} array for the + * available requested energy consumers (power models). + * + * @param energyConsumerIds Array of {@link EnergyConsumerId} for which energy consumed is being + * requested. + * + * @return A Future containing a list of {@link EnergyConsumerResult} objects containing energy + * consumer results for all listed {@link EnergyConsumerId}. + */ + public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( + @EnergyConsumerId int[] energyConsumerIds); +} diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index a6d9bf8bc55b..f04af8bbf1a0 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -73,7 +73,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.SortedSet; @@ -446,7 +445,10 @@ public final class DropBoxManagerService extends SystemService { // from an in-memory buffer, or another file on disk; if we buffered // we'd lose out on sendfile() optimizations if (forceCompress) { - FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd))); + final GZIPOutputStream gzipOutputStream = + new GZIPOutputStream(new FileOutputStream(fd)); + FileUtils.copy(in, gzipOutputStream); + gzipOutputStream.finish(); } else { FileUtils.copy(in, new FileOutputStream(fd)); } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index dfcc32505b9b..b14ce1cc412a 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -5,7 +5,7 @@ per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkMan per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com # Zram writeback -per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com +per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com # Userspace reboot per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com @@ -30,6 +30,7 @@ per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWN per-file MmsServiceBroker.java = file:/telephony/OWNERS per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS +per-file PinnerService.java = file:/apct-tests/perftests/OWNERS per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index a3bcbbe25e88..871de0dc28bf 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -43,6 +43,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -61,7 +63,6 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.SystemService.TargetUser; import com.android.server.wm.ActivityTaskManagerInternal; import dalvik.system.DexFile; @@ -70,6 +71,7 @@ import dalvik.system.VMRuntime; import java.io.Closeable; import java.io.DataInputStream; import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -100,12 +102,6 @@ public final class PinnerService extends SystemService { private static final int KEY_HOME = 1; private static final int KEY_ASSISTANT = 2; - // Pin the camera application. Default to the system property only if the experiment phenotype - // property is not set. - private static boolean PROP_PIN_CAMERA = - DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, - "pin_camera", - SystemProperties.getBoolean("pinner.pin_camera", false)); // Pin using pinlist.meta when pinning apps. private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean( "pinner.use_pinlist", true); @@ -150,7 +146,13 @@ public final class PinnerService extends SystemService { /** * A set of {@link AppKey} that are configured to be pinned. */ - private final ArraySet<Integer> mPinKeys = new ArraySet<>(); + @GuardedBy("this") + private ArraySet<Integer> mPinKeys; + + // Resource-configured pinner flags; + private final boolean mConfiguredToPinCamera; + private final boolean mConfiguredToPinHome; + private final boolean mConfiguredToPinAssistant; private BinderService mBinderService; private PinnerHandler mPinnerHandler = null; @@ -173,25 +175,13 @@ public final class PinnerService extends SystemService { super(context); mContext = context; - boolean shouldPinCamera = context.getResources().getBoolean( + mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); - boolean shouldPinHome = context.getResources().getBoolean( + mConfiguredToPinHome = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerHomeApp); - boolean shouldPinAssistant = context.getResources().getBoolean( + mConfiguredToPinAssistant = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerAssistantApp); - if (shouldPinCamera) { - if (PROP_PIN_CAMERA) { - mPinKeys.add(KEY_CAMERA); - } else if (DEBUG) { - Slog.i(TAG, "Pinner - skip pinning camera app"); - } - } - if (shouldPinHome) { - mPinKeys.add(KEY_HOME); - } - if (shouldPinAssistant) { - mPinKeys.add(KEY_ASSISTANT); - } + mPinKeys = createPinKeys(); mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); @@ -259,9 +249,10 @@ public final class PinnerService extends SystemService { * The other files pinned in onStart will not need to be updated. */ public void update(ArraySet<String> updatedPackages, boolean force) { + ArraySet<Integer> pinKeys = getPinKeys(); int currentUser = ActivityManager.getCurrentUser(); - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.valueAt(i); ApplicationInfo info = getInfoForKey(key, currentUser); if (info != null && updatedPackages.contains(info.packageName)) { Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force); @@ -385,6 +376,14 @@ public final class PinnerService extends SystemService { } } + private void unpinApps() { + ArraySet<Integer> pinKeys = getPinKeys(); + for (int i = pinKeys.size() - 1; i >= 0; i--) { + int key = pinKeys.valueAt(i); + unpinApp(key); + } + } + private void unpinApp(@AppKey int key) { ArrayList<PinnedFile> pinnedAppFiles; synchronized (this) { @@ -490,9 +489,79 @@ public final class PinnerService extends SystemService { userHandle)); } + private void sendPinAppsWithUpdatedKeysMessage(int userHandle) { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys, + this, userHandle)); + } + private void sendUnpinAppsMessage() { + mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this)); + } + + private ArraySet<Integer> createPinKeys() { + ArraySet<Integer> pinKeys = new ArraySet<>(); + // Pin the camera application. Default to the system property only if the experiment + // phenotype property is not set. + boolean shouldPinCamera = mConfiguredToPinCamera + && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + "pin_camera", + SystemProperties.getBoolean("pinner.pin_camera", false)); + if (shouldPinCamera) { + pinKeys.add(KEY_CAMERA); + } else if (DEBUG) { + Slog.i(TAG, "Pinner - skip pinning camera app"); + } + + if (mConfiguredToPinHome) { + pinKeys.add(KEY_HOME); + } + if (mConfiguredToPinAssistant) { + pinKeys.add(KEY_ASSISTANT); + } + + return pinKeys; + } + + private static boolean shouldPinSplitApks() { + // For now this is disabled by default bcause the pinlist support for split APKs are + // missing in the toolchain. This flag should be removed once it is ready. b/174697187. + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + "pin_split_apks", false); + } + + private synchronized ArraySet<Integer> getPinKeys() { + return mPinKeys; + } + private void pinApps(int userHandle) { - for (int i = mPinKeys.size() - 1; i >= 0; i--) { - int key = mPinKeys.valueAt(i); + pinAppsInternal(userHandle, false); + } + + private void pinAppsWithUpdatedKeys(int userHandle) { + pinAppsInternal(userHandle, true); + } + + /** + * @param updateKeys True if the pinned app list has to be updated. This is true only when + * "pinner repin" shell command is requested. + */ + private void pinAppsInternal(int userHandle, boolean updateKeys) { + if (updateKeys) { + ArraySet<Integer> newKeys = createPinKeys(); + synchronized (this) { + // This code path demands preceding unpinApps() call. + if (!mPinnedApps.isEmpty()) { + Slog.e(TAG, "Attempted to update a list of apps, " + + "but apps were already pinned. Skipping."); + return; + } + + mPinKeys = newKeys; + } + } + + ArraySet<Integer> currentPinKeys = getPinKeys(); + for (int i = currentPinKeys.size() - 1; i >= 0; i--) { + int key = currentPinKeys.valueAt(i); pinApp(key, userHandle, true /* force */); } } @@ -610,19 +679,40 @@ public final class PinnerService extends SystemService { mPinnedApps.put(key, pinnedApp); } + // pin APK - int pinSizeLimit = getSizeLimitForKey(key); - String apk = appInfo.sourceDir; - PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true); - if (pf == null) { - Slog.e(TAG, "Failed to pin " + apk); - return; - } - if (DEBUG) { - Slog.i(TAG, "Pinned " + pf.fileName); + final int pinSizeLimit = getSizeLimitForKey(key); + List<String> apks = new ArrayList<>(); + apks.add(appInfo.sourceDir); + + if (shouldPinSplitApks() && appInfo.splitSourceDirs != null) { + for (String splitApk : appInfo.splitSourceDirs) { + apks.add(splitApk); + } } - synchronized (this) { - pinnedApp.mFiles.add(pf); + + int apkPinSizeLimit = pinSizeLimit; + for (String apk: apks) { + if (apkPinSizeLimit <= 0) { + Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk); + // Continue instead of break to print all skipped APK names. + continue; + } + + PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true); + if (pf == null) { + Slog.e(TAG, "Failed to pin " + apk); + continue; + } + + if (DEBUG) { + Slog.i(TAG, "Pinned " + pf.fileName); + } + synchronized (this) { + pinnedApp.mFiles.add(pf); + } + + apkPinSizeLimit -= pf.bytesPinned; } // determine the ABI from either ApplicationInfo or Build @@ -641,7 +731,7 @@ public final class PinnerService extends SystemService { //not pinning the oat/odex is not a fatal error for (String file : files) { - pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false); + PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false); if (pf != null) { synchronized (this) { if (PROP_PIN_ODEX) { @@ -981,6 +1071,42 @@ public final class PinnerService extends SystemService { } } } + + private void repin() { + sendUnpinAppsMessage(); + // TODO(morrita): Consider supporting non-system user. + sendPinAppsWithUpdatedKeysMessage(UserHandle.USER_SYSTEM); + } + + private void printError(FileDescriptor out, String message) { + PrintWriter writer = new PrintWriter(new FileOutputStream(out)); + writer.println(message); + writer.flush(); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + if (args.length < 1) { + printError(out, "Command is not given."); + resultReceiver.send(-1, null); + return; + } + + String command = args[0]; + switch (command) { + case "repin": + repin(); + break; + default: + printError(out, String.format( + "Unknown pinner command: %s. Supported commands: repin", command)); + resultReceiver.send(-1, null); + return; + } + + resultReceiver.send(0, null); + } } private static final class PinnedFile implements AutoCloseable { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index c191a78aad0e..2fdc7965675d 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -26,6 +26,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.vcn.IVcnManagementService; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.os.Binder; import android.os.Handler; @@ -495,4 +496,20 @@ public class VcnManagementService extends IVcnManagementService.Stub { return Collections.unmodifiableMap(mVcns); } } + + /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ + @Override + public void addVcnUnderlyingNetworkPolicyListener( + IVcnUnderlyingNetworkPolicyListener listener) { + // TODO(b/175739863): implement policy listener registration + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ + @Override + public void removeVcnUnderlyingNetworkPolicyListener( + IVcnUnderlyingNetworkPolicyListener listener) { + // TODO(b/175739863): implement policy listener unregistration + throw new UnsupportedOperationException("Not yet implemented"); + } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 88bb1a012a12..5cc32743af34 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1692,7 +1692,7 @@ public final class ActiveServices { } try { - String ignoreForeground = null; + boolean ignoreForeground = false; final int mode = mAm.getAppOpsManager().checkOpNoThrow( AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); switch (mode) { @@ -1702,9 +1702,9 @@ public final class ActiveServices { break; case AppOpsManager.MODE_IGNORED: // Whoops, silently ignore this. - ignoreForeground = "Service.startForeground() not allowed due to app op: " - + "service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, "Service.startForeground() not allowed due to app op: service " + + r.shortInstanceName); + ignoreForeground = true; break; default: throw new SecurityException("Foreground not allowed as per app op"); @@ -1712,18 +1712,19 @@ public final class ActiveServices { // Apps that are TOP or effectively similar may call startForeground() on // their services even if they are restricted from doing that while in bg. - if (ignoreForeground == null + if (!ignoreForeground && !appIsTopLocked(r.appInfo.uid) && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { - ignoreForeground = "Service.startForeground() not allowed due to bg restriction" - + ":service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, + "Service.startForeground() not allowed due to bg restriction: service " + + r.shortInstanceName); // Back off of any foreground expectations around this service, since we've // just turned down its fg request. updateServiceForegroundLocked(r.app, false); + ignoreForeground = true; } - if (ignoreForeground == null) { + if (!ignoreForeground) { if (isFgsBgStart(r.mAllowStartForeground)) { if (!r.mLoggedInfoAllowStartForeground) { Slog.wtf(TAG, "Background started FGS " @@ -1732,12 +1733,17 @@ public final class ActiveServices { } if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { - ignoreForeground = "Service.startForeground() not allowed due to " + final String msg = "Service.startForeground() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; - Slog.w(TAG, ignoreForeground); + Slog.w(TAG, msg); showFgsBgRestrictedNotificationLocked(r); updateServiceForegroundLocked(r.app, true); + ignoreForeground = true; + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, + r.appInfo.uid)) { + throw new IllegalStateException(msg); + } } } } @@ -1746,7 +1752,7 @@ public final class ActiveServices { // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app // is not restricted. - if (ignoreForeground == null) { + if (!ignoreForeground) { if (r.foregroundId != id) { cancelForegroundNotificationLocked(r); r.foregroundId = id; @@ -1808,10 +1814,6 @@ public final class ActiveServices { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); } - if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid) - && isBgFgsRestrictionEnabled(r)) { - throw new IllegalStateException(ignoreForeground); - } } } finally { if (stopProcStatsOp) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 16874cfb5819..1f48aeb91de8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -32,6 +32,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; +import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; @@ -1085,11 +1086,13 @@ public class ActivityManagerService extends IActivityManager.Stub final int targetUid; final long duration; final String tag; + final int type; - PendingTempWhitelist(int _targetUid, long _duration, String _tag) { + PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) { targetUid = _targetUid; duration = _duration; tag = _tag; + type = _type; } void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -1097,6 +1100,7 @@ public class ActivityManagerService extends IActivityManager.Stub proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type); proto.end(token); } } @@ -5526,8 +5530,7 @@ public class ActivityManagerService extends IActivityManager.Stub } boolean isWhitelistedForFgsStartLocked(int uid) { - final int appId = UserHandle.getAppId(uid); - return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0 + return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0 || mFgsStartTempAllowList.isAllowed(uid); } @@ -9298,6 +9301,8 @@ public class ActivityManagerService extends IActivityManager.Stub TimeUtils.formatDuration(ptw.duration, pw); pw.print(" "); pw.println(ptw.tag); + pw.print(" "); + pw.print(ptw.type); } } } @@ -15359,11 +15364,12 @@ public class ActivityManagerService extends IActivityManager.Stub */ @GuardedBy("this") void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) { - mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag)); + mPendingTempWhitelist.put(targetUid, + new PendingTempWhitelist(targetUid, duration, tag, type)); setUidTempWhitelistStateLocked(targetUid, true); mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget(); - if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { mFgsStartTempAllowList.add(targetUid, duration); } } @@ -15389,7 +15395,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = 0; i < N; i++) { PendingTempWhitelist ptw = list[i]; mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, - ptw.duration, true, ptw.tag); + ptw.duration, ptw.type, true, ptw.tag); } } @@ -15406,8 +15412,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @GuardedBy("this") - final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { - mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist); + final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { + mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist); } @GuardedBy("this") @@ -15715,6 +15721,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean startProfile(@UserIdInt int userId) { + return mUserController.startProfile(userId); + } + + @Override + public boolean stopProfile(@UserIdInt int userId) { + return mUserController.stopProfile(userId); + } + + @Override public UserInfo getCurrentUser() { return mUserController.getCurrentUser(); } @@ -15999,10 +16015,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) { + public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding, + long durationMs, @BroadcastOptions.TempAllowListType int type) { synchronized (ActivityManagerService.this) { mDeviceIdleTempWhitelist = appids; - setAppIdTempWhitelistStateLocked(changingAppId, adding); + if (adding) { + if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(changingUid, durationMs); + } + } + setAppIdTempWhitelistStateLocked(changingUid, adding); } } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 6fe934ee6937..ada7eeab9da3 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -17,21 +17,24 @@ package com.android.server.am; import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY; +import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothAdapter; import android.content.Context; +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; import android.net.wifi.WifiManager; import android.os.BatteryStats; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.Parcelable; import android.os.Process; -import android.os.ServiceManager; import android.os.SynchronousResultReceiver; import android.os.SystemClock; import android.os.ThreadLocalWorkSource; import android.os.connectivity.WifiActivityEnergyInfo; +import android.power.PowerStatsInternal; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.IntArray; @@ -41,8 +44,10 @@ import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.power.MeasuredEnergyArray; +import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; import libcore.util.EmptyArray; @@ -91,6 +96,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { }); private final Context mContext; + + @GuardedBy("mStats") private final BatteryStatsImpl mStats; @GuardedBy("this") @@ -123,6 +130,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("this") private Future<?> mBatteryLevelSync; + // If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first. private final Object mWorkerLock = new Object(); @GuardedBy("mWorkerLock") @@ -131,6 +139,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("mWorkerLock") private TelephonyManager mTelephony = null; + @GuardedBy("mWorkerLock") + private PowerStatsInternal mPowerStatsInternal = null; + // WiFi keeps an accumulated total of stats, unlike Bluetooth. // Keep the last WiFi stats so we can compute a delta. @GuardedBy("mWorkerLock") @@ -139,7 +150,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { /** Snapshot of measured energies, or null if no measured energies are supported. */ @GuardedBy("mWorkerLock") - private final @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot; + private @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot = null; /** * Timestamp at which all external stats were last collected in @@ -148,13 +159,27 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { @GuardedBy("this") private long mLastCollectionTimeStamp; - BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats, - @Nullable MeasuredEnergyArray initialMeasuredEnergies) { + BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) { mContext = context; mStats = stats; + } - mMeasuredEnergySnapshot = initialMeasuredEnergies == null ? - null : new MeasuredEnergySnapshot(initialMeasuredEnergies); + public void systemServicesReady() { + final WifiManager wm = mContext.getSystemService(WifiManager.class); + final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + final PowerStatsInternal psi = LocalServices.getService(PowerStatsInternal.class); + final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(psi); + final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialMeasuredEnergies); + synchronized (mWorkerLock) { + mWifiManager = wm; + mTelephony = tm; + mPowerStatsInternal = psi; + mMeasuredEnergySnapshot = initialMeasuredEnergies == null + ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies); + synchronized (mStats) { + mStats.initMeasuredEnergyStatsLocked(supportedBuckets); + } + } } @Override @@ -433,14 +458,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. - if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) { - // this code is reached very early in the boot process, before Wifi Service has - // been registered. Check that ServiceManager.getService() returns a non null - // value before calling mContext.getSystemService(), since otherwise - // getSystemService() will throw a ServiceNotFoundException. - mWifiManager = mContext.getSystemService(WifiManager.class); - } - // Only fetch WiFi power data if it is supported. if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) { SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi"); @@ -478,10 +495,6 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) { // We were asked to fetch Telephony data. - if (mTelephony == null) { - mTelephony = mContext.getSystemService(TelephonyManager.class); - } - if (mTelephony != null) { CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>(); mTelephony.requestModemActivityInfo(Runnable::run, @@ -689,17 +702,91 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { return delta; } - // TODO(b/172934873): Evaluate a safe way to query the HAL without holding mStats + /** + * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to + * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s. + * + * @return array with true for index i if energy bucket i is supported. + */ + private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) { + if (energyArray == null) { + return null; + } + final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; + final int size = energyArray.size(); + for (int energyIdx = 0; energyIdx < size; energyIdx++) { + switch (energyArray.getSubsystem(energyIdx)) { + case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true; + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true; + buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true; + break; + } + } + return buckets; + } + + /** + * Get a {@link MeasuredEnergyArray} with the latest + * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot. + * + * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link + * EnergyConsumerResult}[] + */ + private static @Nullable + MeasuredEnergyArray getEnergyConsumptionData(@NonNull PowerStatsInternal psi) { + final EnergyConsumerResult[] results; + try { + results = psi.getEnergyConsumedAsync(new int[0]) + .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Slog.e(TAG, "Failed to getEnergyConsumedAsync", e); + return null; + } + if (results == null) return null; + final int size = results.length; + final int[] subsystems = new int[size]; + final long[] energyUJ = new long[size]; + + for (int i = 0; i < size; i++) { + final EnergyConsumerResult consumer = results[i]; + final int subsystem; + switch (consumer.energyConsumerId) { + case EnergyConsumerId.DISPLAY: + subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + break; + default: + continue; + } + subsystems[i] = subsystem; + energyUJ[i] = consumer.energyUWs; + } + return new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + + @Override + public long getEnergy(int index) { + return energyUJ[index]; + } + + @Override + public int size() { + return size; + } + }; + } + /** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */ @GuardedBy("mWorkerLock") private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) { - if (mMeasuredEnergySnapshot == null) return null; + if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null; if (flags == UPDATE_ALL) { // Gotta catch 'em all... including custom (non-specific) subsystems - synchronized (mStats) { - return mStats.getEnergyConsumptionDataLocked(); - } + return getEnergyConsumptionData(mPowerStatsInternal); } final List<Integer> energyConsumerIds = new ArrayList<>(); @@ -710,10 +797,8 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (energyConsumerIds.isEmpty()) { return null; } - synchronized (mStats) { - // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray() - return mStats.getEnergyConsumptionDataLocked(); - } + // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray() + return getEnergyConsumptionData(mPowerStatsInternal); } @GuardedBy("mWorkerLock") diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 2f7c5234d982..405ee7266578 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -16,14 +16,11 @@ package com.android.server.am; -import android.annotation.Nullable; import android.bluetooth.BluetoothActivityEnergyInfo; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.hardware.power.stats.EnergyConsumerId; -import android.hardware.power.stats.EnergyConsumerResult; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryUsageStats; @@ -65,9 +62,6 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; -import com.android.internal.power.MeasuredEnergyArray; -import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem; -import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -75,7 +69,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.UserManagerInternal; -import com.android.server.powerstats.PowerStatsHALWrapper; import java.io.File; import java.io.FileDescriptor; @@ -126,8 +119,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); private static final int MAX_LOW_POWER_STATS_SIZE = 4096; - private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper; - private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); @@ -199,43 +190,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - @Override - public @Nullable MeasuredEnergyArray getEnergyConsumptionData() { - final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); - if (results == null) return null; - final int size = results.length; - final int[] subsystems = new int[size]; - final long[] energyUJ = new long[size]; - - for (int i = 0; i < size; i++) { - final EnergyConsumerResult consumer = results[i]; - final int subsystem; - switch (consumer.energyConsumerId) { - case EnergyConsumerId.DISPLAY: - subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; - break; - default: - continue; - } - subsystems[i] = subsystem; - energyUJ[i] = consumer.energyUWs; - } - return new MeasuredEnergyArray() { - @Override - public int getSubsystem(int index) { - return subsystems[index]; - } - @Override - public long getEnergy(int index) { - return energyUJ[index]; - } - @Override - public int size() { - return size; - } - }; - } - BatteryStatsService(Context context, File systemDir, Handler handler) { // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through. mContext = context; @@ -253,14 +207,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); - // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries - mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); - - final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData(); - final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies); mStats = new BatteryStatsImpl(systemDir, handler, this, - this, supportedBuckets, mUserManagerUserInfoProvider); - mWorker = new BatteryExternalStatsWorker(context, mStats, initialEnergies); + this, mUserManagerUserInfoProvider); + mWorker = new BatteryExternalStatsWorker(context, mStats); mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); @@ -268,30 +217,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats); } - /** - * Map the {@link MeasuredEnergySubsystem}s in the given energyArray to their corresponding - * {@link MeasuredEnergyStats.EnergyBucket}s. - * - * @return array with true for index i if energy bucket i is supported. - */ - private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) { - if (energyArray == null) { - return null; - } - final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS]; - final int size = energyArray.size(); - for (int energyIdx = 0; energyIdx < size; energyIdx++) { - switch (energyArray.getSubsystem(energyIdx)) { - case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true; - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true; - buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true; - break; - } - } - return buckets; - } - public void publish() { LocalServices.addService(BatteryStatsInternal.class, new LocalService()); ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder()); @@ -299,6 +224,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void systemServicesReady() { mStats.systemServicesReady(mContext); + mWorker.systemServicesReady(); Watchdog.getInstance().addMonitor(this); } @@ -2270,7 +2196,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( null, mStats.mHandler, null, null, - null /* energy buckets not currently in checkin anyway */, mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); @@ -2311,7 +2236,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( null, mStats.mHandler, null, null, - null /* energy buckets not currently in checkin anyway */, mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java index 1f9039392405..3aca4cfd0162 100644 --- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java @@ -28,7 +28,7 @@ import android.util.SparseLongArray; final class FgsStartTempAllowList { private static final int MAX_SIZE = 100; /** - * The key is the UID, the value is expiration elapse time in ms of this temp-allowed UID. + * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid. */ private final SparseLongArray mTempAllowListFgs = new SparseLongArray(); @@ -37,7 +37,8 @@ final class FgsStartTempAllowList { void add(int uid, long duration) { if (duration <= 0) { - Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid); + Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + + uid); return; } // The temp allowlist should be a short list with only a few entries in it. diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index d69bf2d75064..de7931535297 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -92,7 +92,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; -import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LongSparseArray; @@ -2731,11 +2730,11 @@ public final class OomAdjuster { } @GuardedBy("mService") - final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { + final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) { boolean changed = false; for (int i = mActiveUids.size() - 1; i >= 0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); - if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) { + if (uidRec.uid == uid && uidRec.curWhitelist != onWhitelist) { uidRec.curWhitelist = onWhitelist; changed = true; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index c04f6ff37229..d73de7c309f0 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -628,19 +628,30 @@ class UserController implements Handler.Callback { Binder.getCallingUid(), Binder.getCallingPid(), userId); } - if (getUserInfo(userId).isManagedProfile()) { + final UserInfo userInfo = getUserInfo(userId); + if (userInfo.isProfile()) { UserInfo parent = mInjector.getUserManager().getProfileParent(userId); if (parent != null) { - final Intent profileUnlockedIntent = new Intent( - Intent.ACTION_MANAGED_PROFILE_UNLOCKED); - profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); - profileUnlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntent(profileUnlockedIntent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), parent.id); + // Send PROFILE_ACCESSIBLE broadcast to the parent user if a profile was unlocked + broadcastProfileAccessibleStateChanged(userId, parent.id, + Intent.ACTION_PROFILE_ACCESSIBLE); + + //TODO(b/175704931): send ACTION_MANAGED_PROFILE_AVAILABLE + + // Also send MANAGED_PROFILE_UNLOCKED broadcast to the parent user + // if a managed profile was unlocked + if (userInfo.isManagedProfile()) { + final Intent profileUnlockedIntent = new Intent( + Intent.ACTION_MANAGED_PROFILE_UNLOCKED); + profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + profileUnlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(profileUnlockedIntent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), parent.id); + } } } @@ -761,13 +772,44 @@ class UserController implements Handler.Callback { int restartUser(final int userId, final boolean foreground) { return stopUser(userId, /* force= */ true, /* allowDelayedLocking= */ false, /* stopUserCallback= */ null, new KeyEvictedCallback() { - @Override - public void keyEvicted(@UserIdInt int userId) { - // Post to the same handler that this callback is called from to ensure the user - // cleanup is complete before restarting. - mHandler.post(() -> UserController.this.startUser(userId, foreground)); - } - }); + @Override + public void keyEvicted(@UserIdInt int userId) { + // Post to the same handler that this callback is called from to ensure + // the user cleanup is complete before restarting. + mHandler.post(() -> UserController.this.startUser(userId, foreground)); + } + }); + } + + /** + * Stops a user only if it's a profile, with a more relaxed permission requirement: + * {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * To be called from ActivityManagerService. + * @param userId the id of the user to stop. + * @return true if the operation was successful. + */ + boolean stopProfile(final @UserIdInt int userId) { + if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS) + == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException( + "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to " + + "stop a profile"); + } + + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null || !userInfo.isProfile()) { + throw new IllegalArgumentException("User " + userId + " is not a profile"); + } + + enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); + synchronized (mLock) { + return stopUsersLU(userId, /* force= */ true, /* allowDelayedLocking= */ + false, /* stopUserCallback= */ null, /* keyEvictedCallback= */ null) + == ActivityManager.USER_OP_SUCCESS; + } } int stopUser(final int userId, final boolean force, boolean allowDelayedLocking, @@ -1144,6 +1186,17 @@ class UserController implements Handler.Callback { null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); + + // Send PROFILE_INACCESSIBLE broadcast if a profile was stopped + final UserInfo userInfo = getUserInfo(userId); + if (userInfo.isProfile()) { + UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null) { + broadcastProfileAccessibleStateChanged(userId, parent.id, + Intent.ACTION_PROFILE_INACCESSIBLE); + //TODO(b/175704931): send ACTION_MANAGED_PROFILE_UNAVAILABLE + } + } } /** @@ -1208,6 +1261,37 @@ class UserController implements Handler.Callback { } } + /** + * Starts a user only if it's a profile, with a more relaxed permission requirement: + * {@link android.Manifest.permission#MANAGE_USERS} or + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * To be called from ActivityManagerService. + * @param userId the id of the user to start. + * @return true if the operation was successful. + */ + boolean startProfile(final @UserIdInt int userId) { + if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS) + == PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException( + "You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to " + + "start a profile"); + } + + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null || !userInfo.isProfile()) { + throw new IllegalArgumentException("User " + userId + " is not a profile"); + } + + if (!userInfo.isEnabled()) { + Slog.w(TAG, "Cannot start disabled profile #" + userId); + return false; + } + + return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null); + } + boolean startUser(final @UserIdInt int userId, final boolean foreground) { return startUser(userId, foreground, null); } @@ -1248,9 +1332,13 @@ class UserController implements Handler.Callback { final @UserIdInt int userId, final boolean foreground, @Nullable IProgressListener unlockListener) { - checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "startUser"); + return startUserNoChecks(userId, foreground, unlockListener); + } + + private boolean startUserNoChecks(final @UserIdInt int userId, final boolean foreground, + @Nullable IProgressListener unlockListener) { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg")); @@ -1872,6 +1960,25 @@ class UserController implements Handler.Callback { } } + /** + * Broadcasts to the parent user when a profile is started+unlocked/stopped. + * @param userId the id of the profile + * @param parentId the id of the parent user + * @param intentAction either ACTION_PROFILE_ACCESSIBLE or ACTION_PROFILE_INACCESSIBLE + */ + private void broadcastProfileAccessibleStateChanged(@UserIdInt int userId, + @UserIdInt int parentId, + String intentAction) { + final Intent intent = new Intent(intentAction); + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */ + null, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ + null, /* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */ + null, /* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), parentId); + } int handleIncomingUser(int callingPid, int callingUid, @UserIdInt int userId, boolean allowAll, int allowMode, String name, String callerPackage) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 676fcd0bdc87..17fd32c57e09 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -1488,7 +1488,7 @@ final class HistoricalRegistry { private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps, @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_PACKAGE); - serializer.attribute(null, ATTR_NAME, packageOps.getPackageName()); + serializer.attributeInterned(null, ATTR_NAME, packageOps.getPackageName()); final int numAttributions = packageOps.getAttributedOpsCount(); for (int i = 0; i < numAttributions; i++) { final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 82dc16133a0c..c413c8b6362e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -79,6 +79,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override protected void startHalOperation() { UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_AUTH, mUdfpsOverlayController); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index cacc3661b1d8..0864c1a69a6f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -71,6 +71,13 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override protected boolean hasReachedEnrollmentLimit() { return FingerprintUtils.getInstance(getSensorId()) .getBiometricsForUser(getContext(), getTargetUserId()).size() diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 784e37bed553..13e2e4fd7a37 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -101,6 +101,13 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi } } + @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + private void resetFailedAttempts(int userId) { mLockoutFrameworkImpl.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index b2e3c3302bbf..8493af13abd4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -113,6 +113,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } @Override + public void onError(int errorCode, int vendorCode) { + super.onError(errorCode, vendorCode); + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + } + + @Override public void onPointerDown(int x, int y, float minor, float major) { UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major); } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 633c0c417192..a0d9e8e0a6fb 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -26,7 +26,6 @@ import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; import android.system.ErrnoException; import android.text.FontConfig; -import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -40,7 +39,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.NioUtils; import java.nio.channels.FileChannel; -import java.util.HashMap; import java.util.Map; /** A service for managing system fonts. */ @@ -69,7 +67,10 @@ public final class FontManagerService { @Override @Nullable public SharedMemory getSerializedSystemFontMap() { - return mService.getSerializedSystemFontMap(); + if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return null; + } + return mService.getCurrentFontSettings().getSerializedSystemFontMap(); } }); } @@ -98,48 +99,20 @@ public final class FontManagerService { private final UpdatableFontDir mUpdatableFontDir; @GuardedBy("FontManagerService.this") - @Nullable - private SharedMemory mSerializedSystemFontMap = null; + @Nullable SystemFontSettings mCurrentFontSettings = null; private FontManagerService() { mUpdatableFontDir = ENABLE_FONT_UPDATES ? new UpdatableFontDir(new File(FONT_FILES_DIR), new OtfFontFileParser()) : null; } - @Nullable - private SharedMemory getSerializedSystemFontMap() { - if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { - return null; - } + @NonNull private SystemFontSettings getCurrentFontSettings() { synchronized (FontManagerService.this) { - if (mSerializedSystemFontMap == null) { - mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); - } - return mSerializedSystemFontMap; - } - } - - @Nullable - private SharedMemory createSerializedSystemFontMapLocked() { - if (mUpdatableFontDir != null) { - HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Map<String, File> fontFileMap = mUpdatableFontDir.getFontFileMap(); - Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = - SystemFonts.initializeSystemFonts(fontFileMap); - Typeface.initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); - try { - return Typeface.serializeFontMap(systemFontMap); - } catch (IOException | ErrnoException e) { - Slog.w(TAG, "Failed to serialize updatable font map. " - + "Retrying with system image fonts.", e); + if (mCurrentFontSettings == null) { + mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir); } + return mCurrentFontSettings; } - try { - return Typeface.serializeFontMap(Typeface.getSystemFontMap()); - } catch (IOException | ErrnoException e) { - Slog.e(TAG, "Failed to serialize SystemServer system font map", e); - } - return null; } private boolean installFontFile(String name, FileDescriptor fd) { @@ -152,8 +125,74 @@ public final class FontManagerService { return false; } // Create updated font map in the next getSerializedSystemFontMap() call. - mSerializedSystemFontMap = null; + mCurrentFontSettings = null; return true; } } + + private static class SystemFontSettings { + private final @NonNull SharedMemory mSerializedSystemFontMap; + private final @NonNull FontConfig mSystemFontConfig; + private final @NonNull Map<String, FontFamily[]> mSystemFallbackMap; + private final @NonNull Map<String, Typeface> mSystemTypefaceMap; + + SystemFontSettings( + @NonNull SharedMemory serializedSystemFontMap, + @NonNull FontConfig systemFontConfig, + @NonNull Map<String, FontFamily[]> systemFallbackMap, + @NonNull Map<String, Typeface> systemTypefaceMap) { + mSerializedSystemFontMap = serializedSystemFontMap; + mSystemFontConfig = systemFontConfig; + mSystemFallbackMap = systemFallbackMap; + mSystemTypefaceMap = systemTypefaceMap; + } + + public @NonNull SharedMemory getSerializedSystemFontMap() { + return mSerializedSystemFontMap; + } + + public @NonNull FontConfig getSystemFontConfig() { + return mSystemFontConfig; + } + + public @NonNull Map<String, FontFamily[]> getSystemFallbackMap() { + return mSystemFallbackMap; + } + + public @NonNull Map<String, Typeface> getSystemTypefaceMap() { + return mSystemTypefaceMap; + } + + public static @Nullable SystemFontSettings create( + @Nullable UpdatableFontDir updatableFontDir) { + if (updatableFontDir != null) { + final FontConfig fontConfig = SystemFonts.getSystemFontConfig( + updatableFontDir.getFontFileMap()); + final Map<String, FontFamily[]> fallback = + SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.w(TAG, "Failed to serialize updatable font map. " + + "Retrying with system image fonts.", e); + } + } + + final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); + try { + final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); + return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + } + return null; + } + }; } diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java index 2a3cc907d675..5fa799839789 100644 --- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java @@ -22,6 +22,7 @@ import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; import android.media.metrics.PlaybackStateEvent; +import android.media.metrics.TrackChangeEvent; import android.os.Binder; import android.util.Base64; import android.util.StatsEvent; @@ -120,5 +121,30 @@ public final class PlaybackMetricsManagerService extends SystemService { .build(); StatsLog.write(statsEvent); } + + @Override + public void reportTrackChangeEvent( + String sessionId, TrackChangeEvent event, int userId) { + StatsEvent statsEvent = StatsEvent.newBuilder() + .setAtomId(321) + .writeString(sessionId) + .writeInt(event.getTrackState()) + .writeInt(event.getTrackChangeReason()) + .writeString(event.getContainerMimeType()) + .writeString(event.getSampleMimeType()) + .writeString(event.getCodecName()) + .writeInt(event.getBitrate()) + .writeLong(event.getTimeSincePlaybackCreatedMillis()) + .writeInt(event.getTrackType()) + .writeString(event.getLanguage()) + .writeString(event.getLanguageRegion()) + .writeInt(event.getChannelCount()) + .writeInt(event.getSampleRate()) + .writeInt(event.getWidth()) + .writeInt(event.getHeight()) + .usePooledBuffer() + .build(); + StatsLog.write(statsEvent); + } } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index b65fc7358471..2b5c393e7159 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -71,6 +71,7 @@ import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.content.pm.PackageInfo; @@ -186,6 +187,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { static final String TAG_CHILD_SESSION = "childSession"; static final String TAG_SESSION_FILE = "sessionFile"; static final String TAG_SESSION_CHECKSUM = "sessionChecksum"; + static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature"; private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission"; private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION = "whitelisted-restricted-permission"; @@ -319,6 +321,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private float mProgress = 0; @GuardedBy("mLock") private float mReportedProgress = -1; + @GuardedBy("mLock") + private float mIncrementalProgress = 0; /** State of the session. */ @GuardedBy("mLock") @@ -397,8 +401,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private ArraySet<FileEntry> mFiles = new ArraySet<>(); + static class PerFileChecksum { + private final Checksum[] mChecksums; + private final byte[] mSignature; + + PerFileChecksum(Checksum[] checksums, byte[] signature) { + mChecksums = checksums; + mSignature = signature; + } + + Checksum[] getChecksums() { + return this.mChecksums; + } + + byte[] getSignature() { + return this.mSignature; + } + } + @GuardedBy("mLock") - private ArrayMap<String, Checksum[]> mChecksums = new ArrayMap<>(); + private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>(); @Nullable final StagedSession mStagedSession; @@ -921,7 +943,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { int sessionId, int userId, int installerUid, @NonNull InstallSource installSource, SessionParams params, long createdMillis, long committedMillis, File stageDir, String stageCid, InstallationFile[] files, - ArrayMap<String, List<Checksum>> checksums, + ArrayMap<String, PerFileChecksum> checksums, boolean prepared, boolean committed, boolean destroyed, boolean sealed, @Nullable int[] childSessionIds, int parentSessionId, boolean isReady, boolean isFailed, boolean isApplied, int stagedSessionErrorCode, @@ -967,11 +989,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (checksums != null) { - for (int i = 0, isize = checksums.size(); i < isize; ++i) { - final String fileName = checksums.keyAt(i); - final List<Checksum> fileChecksums = checksums.valueAt(i); - mChecksums.put(fileName, fileChecksums.toArray(new Checksum[fileChecksums.size()])); - } + mChecksums.putAll(checksums); } if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) { @@ -1182,7 +1200,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void computeProgressLocked(boolean forcePublish) { - mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) + // This method is triggered when the client progress is updated or the incremental progress + // is updated. For incremental installs, ignore the progress values reported from client. + // Instead, only use the progress reported by IncFs as the percentage of loading completion. + final float loadingProgress = + isIncrementalInstallation() ? mIncrementalProgress : mClientProgress; + mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f) + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); // Only publish when meaningful change @@ -1253,7 +1276,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override - public void addChecksums(String name, @NonNull Checksum[] checksums) { + public void setChecksums(String name, @NonNull Checksum[] checksums, + @Nullable byte[] signature) { if (checksums.length == 0) { return; } @@ -1269,6 +1293,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalStateException("Can't obtain calling installer's package."); } + if (signature != null && signature.length != 0) { + final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled(); + final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled(); + if (!standardMode || legacyMode) { + Slog.e(TAG, + "Can't enforce checksum's signature: Apk-Verity is disabled or in legacy " + + "mode."); + signature = null; + } + } + synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums"); @@ -1277,7 +1312,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalStateException("Duplicate checksums."); } - mChecksums.put(name, checksums); + mChecksums.put(name, new PerFileChecksum(checksums, signature)); } } @@ -3032,15 +3067,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile); } + private void storeBytesToInstallationFile(final String localPath, final String absolutePath, + final byte[] bytes) throws IOException { + if (!isIncrementalInstallation() || mIncrementalFileStorages == null) { + FileUtils.bytesToFile(absolutePath, bytes); + } else { + mIncrementalFileStorages.makeFile(localPath, bytes); + } + } + @GuardedBy("mLock") private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName) throws PackageManagerException { - final Checksum[] checksums = mChecksums.get(origFile.getName()); - if (checksums == null) { + final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName()); + if (perFileChecksum == null) { return; } mChecksums.remove(origFile.getName()); + final Checksum[] checksums = perFileChecksum.getChecksums(); if (checksums.length == 0) { return; } @@ -3048,14 +3093,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName()); final File targetDigestsFile = new File(stageDir, targetDigestsPath); try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + // Storing and staging checksums. ApkChecksums.writeChecksums(os, checksums); - final byte[] checksumsBytes = os.toByteArray(); + storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(), + os.toByteArray()); + stageFileLocked(targetDigestsFile, targetDigestsFile); - if (!isIncrementalInstallation() || mIncrementalFileStorages == null) { - FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes); - } else { - mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes); + final byte[] signature = perFileChecksum.getSignature(); + if (signature == null || signature.length == 0) { + return; } + + // Storing and staging signature. + final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath( + targetDigestsPath); + final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath); + storeBytesToInstallationFile(targetDigestsSignaturePath, + targetDigestsSignatureFile.getAbsolutePath(), signature); + stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile); } catch (CertificateException e) { throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to encode certificate for " + mPackageName, e); @@ -3063,8 +3118,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed to store digests for " + mPackageName, e); } - - stageFileLocked(targetDigestsFile, targetDigestsFile); } @GuardedBy("mLock") @@ -3704,7 +3757,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, params, statusListener, healthCheckParams, healthListener, addedFiles, - perUidReadTimeouts); + perUidReadTimeouts, + new IPackageLoadingProgressCallback.Stub() { + @Override + public void onPackageLoadingProgressChanged(float progress) { + synchronized (mLock) { + mIncrementalProgress = progress; + computeProgressLocked(true); + } + } + }); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), @@ -4277,7 +4339,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { final String fileName = mChecksums.keyAt(i); - final Checksum[] checksums = mChecksums.valueAt(i); + final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); + final Checksum[] checksums = perFileChecksum.getChecksums(); for (Checksum checksum : checksums) { out.startTag(null, TAG_SESSION_CHECKSUM); writeStringAttribute(out, ATTR_NAME, fileName); @@ -4286,6 +4349,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { out.endTag(null, TAG_SESSION_CHECKSUM); } } + for (int i = 0, isize = mChecksums.size(); i < isize; ++i) { + final String fileName = mChecksums.keyAt(i); + final PerFileChecksum perFileChecksum = mChecksums.valueAt(i); + final byte[] signature = perFileChecksum.getSignature(); + if (signature == null || signature.length == 0) { + continue; + } + out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); + writeStringAttribute(out, ATTR_NAME, fileName); + writeByteArrayAttribute(out, ATTR_SIGNATURE, signature); + out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE); + } + } out.endTag(null, TAG_SESSION); @@ -4401,6 +4477,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { List<Integer> childSessionIds = new ArrayList<>(); List<InstallationFile> files = new ArrayList<>(); ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>(); + ArrayMap<String, byte[]> signatures = new ArrayMap<>(); int outerDepth = in.getDepth(); int type; while ((type = in.next()) != XmlPullParser.END_DOCUMENT @@ -4443,6 +4520,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } fileChecksums.add(checksum); } + if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) { + final String fileName = readStringAttribute(in, ATTR_NAME); + final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE); + signatures.put(fileName, signature); + } } if (grantedRuntimePermissions.size() > 0) { @@ -4471,13 +4553,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY); } + ArrayMap<String, PerFileChecksum> checksumsMap = null; + if (!checksums.isEmpty()) { + checksumsMap = new ArrayMap<>(checksums.size()); + for (int i = 0, isize = checksums.size(); i < isize; ++i) { + final String fileName = checksums.keyAt(i); + final List<Checksum> perFileChecksum = checksums.valueAt(i); + final byte[] perFileSignature = signatures.get(fileName); + checksumsMap.put(fileName, new PerFileChecksum( + perFileChecksum.toArray(new Checksum[perFileChecksum.size()]), + perFileSignature)); + } + } + InstallSource installSource = InstallSource.create(installInitiatingPackageName, installOriginatingPackageName, installerPackageName, installerAttributionTag); - return new PackageInstallerSession(callback, context, pm, sessionProvider, - installerThread, stagingManager, sessionId, userId, installerUid, - installSource, params, createdMillis, committedMillis, stageDir, stageCid, - fileArray, checksums, prepared, committed, destroyed, sealed, childSessionIdsArray, - parentSessionId, isReady, isFailed, isApplied, stagedSessionErrorCode, - stagedSessionErrorMessage); + return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread, + stagingManager, sessionId, userId, installerUid, installSource, params, + createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap, + prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, + isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index de7338f3e440..c93127db7ca8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -464,7 +464,7 @@ import java.util.function.Predicate; /** * Keep track of all those APKs everywhere. * <p> - * Internally there are two important locks: + * Internally there are three important locks: * <ul> * <li>{@link #mLock} is used to guard all in-memory parsed package details * and other related state. It is a fine-grained lock that should only be held @@ -475,6 +475,10 @@ import java.util.function.Predicate; * this lock should never be acquired while already holding {@link #mLock}. * Conversely, it's safe to acquire {@link #mLock} momentarily while already * holding {@link #mInstallLock}. + * <li>{@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot + * itself and the snapshot invalidation flag. This lock should never be acquired while + * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock} + * momentarily while already holding {@link #mSnapshotLock}. * </ul> * Many internal methods rely on the caller to hold the appropriate locks, and * this contract is expressed through method name suffixes: @@ -485,6 +489,8 @@ import java.util.function.Predicate; * <li>fooLPr(): the caller must hold {@link #mLock} for reading * <li>fooLPw(): the caller must hold {@link #mLock} for writing * </ul> + * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}. It + * should not be taken anywhere else or used for any other purpose. * <p> * Because this class is very central to the platform's security; please run all * CTS and unit tests whenever making modifications: @@ -4727,11 +4733,19 @@ public class PackageManagerService extends IPackageManager.Stub // If true, the cached computer object is invalid (the cache is stale). // The attribute is static since it may be set from outside classes. private static volatile boolean sSnapshotInvalid = true; - // If true, the cache is corked. Do not create a new cache but continue to use the + // If true, the cache is corked. Do not create a new cache but continue to use the // existing one. This throttles cache creation during periods of churn in Package // Manager. private static volatile boolean sSnapshotCorked = false; + /** + * This lock is used to make reads from {@link #sSnapshotInvalid} and + * {@link #mSnapshotComputer} atomic inside {@code snapshotComputer()}. This lock is + * not meant to be used outside that method. This lock must be taken before + * {@link #mLock} is taken. + */ + private final Object mSnapshotLock = new Object(); + // A counter of all queries that hit the cache. private AtomicInteger mSnapshotHits = new AtomicInteger(0); @@ -4759,35 +4773,42 @@ public class PackageManagerService extends IPackageManager.Stub if (!SNAPSHOT_ENABLED) { return mLiveComputer; } + if (Thread.holdsLock(mLock)) { + // If the current thread holds mLock then it may have modified state but not + // yet invalidated the snapshot. Always give the thread the live computer. + return mLiveComputer; + } int hits = 0; if (TRACE_CACHES) { hits = mSnapshotHits.incrementAndGet(); } - Computer c = mSnapshotComputer; - if (sSnapshotCorked && (c != null)) { - // Snapshots are corked, which means new ones should not be built right now. - return c; - } - if (sSnapshotInvalid || (c == null)) { - // The snapshot is invalid if it is marked as invalid or if it is null. If it - // is null, then it is currently being rebuilt by rebuildSnapshot(). - synchronized (mLock) { - // Rebuild the snapshot if it is invalid. Note that the snapshot might be - // invalidated as it is rebuilt. However, the snapshot is still - // self-consistent (the lock is being held)and is current as of the time - // this function is entered. - if (sSnapshotInvalid) { - rebuildSnapshot(hits); - } + synchronized (mSnapshotLock) { + Computer c = mSnapshotComputer; + if (sSnapshotCorked && (c != null)) { + // Snapshots are corked, which means new ones should not be built right now. + return c; + } + if (sSnapshotInvalid || (c == null)) { + // The snapshot is invalid if it is marked as invalid or if it is null. If it + // is null, then it is currently being rebuilt by rebuildSnapshot(). + synchronized (mLock) { + // Rebuild the snapshot if it is invalid. Note that the snapshot might be + // invalidated as it is rebuilt. However, the snapshot is still + // self-consistent (the lock is being held)and is current as of the time + // this function is entered. + if (sSnapshotInvalid) { + rebuildSnapshot(hits); + } - // Guaranteed to be non-null. mSnapshotComputer is only be set to null - // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since - // the mLock is held in this block and since rebuildSnapshot() is - // complete, the attribute can not now be null. - c = mSnapshotComputer; + // Guaranteed to be non-null. mSnapshotComputer is only be set to null + // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since + // the mLock is held in this block and since rebuildSnapshot() is + // complete, the attribute can not now be null. + c = mSnapshotComputer; + } } + return c; } - return c; } /** diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 9f07695fcecf..89729b585b14 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1049,6 +1049,7 @@ class ShortcutPackage extends ShortcutPackageItem { } } + // TODO: update resource strings in AppSearch // If this shortcut is not from a manifest, then update all resource IDs // from resource names. (We don't allow resource strings for // non-manifest at the moment, but icons can still be resources.) @@ -1340,6 +1341,7 @@ class ShortcutPackage extends ShortcutPackageItem { * For all the text fields, refresh the string values if they're from resources. */ public void resolveResourceStrings() { + // TODO: update resource strings in AppSearch final ShortcutService s = mShortcutUser.mService; List<ShortcutInfo> changedShortcuts = null; diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 95ce140470f9..3c4457db6cf0 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1565,6 +1565,7 @@ public class ShortcutService extends IShortcutService.Stub { * resource-based strings. */ void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) { + // TODO: update resource names in AppSearch final Resources publisherRes = injectGetResourcesForApplicationAsUser( si.getPackage(), si.getUserId()); if (publisherRes != null) { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 777857209de0..64bddcdd8fe1 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -19,14 +19,19 @@ package com.android.server.powerstats; import android.annotation.Nullable; import android.content.Context; import android.hardware.power.stats.ChannelInfo; +import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.PowerEntityInfo; import android.os.Binder; import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; import android.os.UserHandle; +import android.power.PowerStatsInternal; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.SystemService; import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; @@ -36,6 +41,7 @@ import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.concurrent.CompletableFuture; /** * This class provides a system service that estimates system power usage @@ -55,7 +61,6 @@ public class PowerStatsService extends SystemService { private final Injector mInjector; private Context mContext; - private IPowerStatsHALWrapper mPowerStatsHALWrapper; @Nullable private PowerStatsLogger mPowerStatsLogger; @Nullable @@ -65,6 +70,8 @@ public class PowerStatsService extends SystemService { @VisibleForTesting static class Injector { + private IPowerStatsHALWrapper mPowerStatsHALWrapper; + File createDataStoragePath() { return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), DATA_STORAGE_SUBDIR); @@ -86,6 +93,13 @@ public class PowerStatsService extends SystemService { return PowerStatsHALWrapper.getPowerStatsHalImpl(); } + IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() { + if (mPowerStatsHALWrapper == null) { + mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); + } + return mPowerStatsHALWrapper; + } + PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, String meterFilename, String modelFilename, String residencyFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { @@ -120,15 +134,15 @@ public class PowerStatsService extends SystemService { } } else if (args.length == 0) { pw.println("PowerStatsService dumpsys: available PowerEntityInfos"); - PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo(); + PowerEntityInfo[] powerEntityInfo = getPowerStatsHal().getPowerEntityInfo(); PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw); pw.println("PowerStatsService dumpsys: available ChannelInfos"); - ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo(); + ChannelInfo[] channelInfo = getPowerStatsHal().getEnergyMeterInfo(); ChannelInfoUtils.dumpsys(channelInfo, pw); pw.println("PowerStatsService dumpsys: available EnergyConsumerIds"); - int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo(); + int[] energyConsumerId = getPowerStatsHal().getEnergyConsumerInfo(); EnergyConsumerIdUtils.dumpsys(energyConsumerId, pw); } } @@ -144,27 +158,33 @@ public class PowerStatsService extends SystemService { @Override public void onStart() { + if (getPowerStatsHal().isInitialized()) { + // Only create internal service if PowerStatsHal is available. + publishLocalService(PowerStatsInternal.class, new LocalService()); + } publishBinderService(Context.POWER_STATS_SERVICE, new BinderService()); } private void onSystemServiceReady() { - mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl(); - - if (mPowerStatsHALWrapper.isInitialized()) { - if (DEBUG) Slog.d(TAG, "Starting PowerStatsService"); + if (getPowerStatsHal().isInitialized()) { + if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers"); // Only start logger and triggers if initialization is successful. mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mInjector.createDataStoragePath(), mInjector.createMeterFilename(), mInjector.createModelFilename(), mInjector.createResidencyFilename(), - mPowerStatsHALWrapper); + getPowerStatsHal()); mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); } else { - Slog.e(TAG, "Initialization of PowerStatsHAL wrapper failed"); + Slog.e(TAG, "Failed to start PowerStatsService loggers"); } } + private IPowerStatsHALWrapper getPowerStatsHal() { + return mInjector.getPowerStatsHALWrapperImpl(); + } + public PowerStatsService(Context context) { this(context, new Injector()); } @@ -175,4 +195,29 @@ public class PowerStatsService extends SystemService { mContext = context; mInjector = injector; } + + private final class LocalService extends PowerStatsInternal { + private final Handler mHandler; + + LocalService() { + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mHandler = new Handler(thread.getLooper()); + } + + @Override + public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( + int[] energyConsumerIds) { + final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture<>(); + mHandler.sendMessage( + PooledLambda.obtainMessage(PowerStatsService.this::getEnergyConsumedAsync, + future, energyConsumerIds)); + return future; + } + } + + private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future, + int[] energyConsumerIds) { + future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds)); + } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 00ab973b9c90..7523671fb3a7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -23,6 +23,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import com.android.internal.view.AppearanceRegion; import com.android.server.notification.NotificationDelegate; @@ -84,7 +85,6 @@ public interface StatusBarManagerInternal { void startAssist(Bundle args); void onCameraLaunchGestureDetected(int source); - void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive); void setDisableFlags(int displayId, int flags, String cause); void toggleSplitScreen(); void appTransitionFinished(int displayId); @@ -128,9 +128,10 @@ public interface StatusBarManagerInternal { */ void onRecentsAnimationStateChanged(boolean running); - /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAppearanceChanged */ - void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme); + /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */ + void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen); /** @see com.android.internal.statusbar.IStatusBar#showTransient */ void showTransient(int displayId, @InternalInsetsType int[] types); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3ee8dd7e9d81..6306c5cdd082 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -57,6 +57,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsetsController.Appearance; +import android.view.WindowInsetsController.Behavior; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -302,11 +303,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive); - } - - @Override public void setDisableFlags(int displayId, int flags, String cause) { StatusBarManagerService.this.setDisableFlags(displayId, flags, cause); } @@ -521,16 +517,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - final UiState state = getUiState(displayId); - if (!state.appearanceEquals(appearance, appearanceRegions, navbarColorManagedByIme)) { - state.setAppearance(appearance, appearanceRegions, navbarColorManagedByIme); - } + public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { + getUiState(displayId).setBarAttributes(appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); if (mBar != null) { try { - mBar.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions, - navbarColorManagedByIme); + mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + navbarColorManagedByIme, behavior, isFullscreen); } catch (RemoteException ex) { } } } @@ -981,27 +976,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } - /** - * Enables System UI to know whether the top app is fullscreen or not, and whether this app is - * in immersive mode or not. - */ - private void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { - enforceStatusBar(); - - synchronized(mLock) { - getUiState(displayId).setFullscreen(isFullscreen); - getUiState(displayId).setImmersive(isImmersive); - mHandler.post(() -> { - if (mBar != null) { - try { - mBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); - } catch (RemoteException ex) { - } - } - }); - } - } - @Override public void setImeWindowStatus(int displayId, final IBinder token, final int vis, final int backDisposition, final boolean showImeSwitcher, @@ -1068,8 +1042,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0]; private ArraySet<Integer> mTransientBarTypes = new ArraySet<>(); private boolean mNavbarColorManagedByIme = false; + private @Behavior int mBehavior; private boolean mFullscreen = false; - private boolean mImmersive = false; private int mDisabled1 = 0; private int mDisabled2 = 0; private int mImeWindowVis = 0; @@ -1077,25 +1051,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; - private void setAppearance(@Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { + private void setBarAttributes(@Appearance int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, + @Behavior int behavior, boolean isFullscreen) { mAppearance = appearance; mAppearanceRegions = appearanceRegions; mNavbarColorManagedByIme = navbarColorManagedByIme; - } - - private boolean appearanceEquals(@Appearance int appearance, - AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { - if (mAppearance != appearance || mAppearanceRegions.length != appearanceRegions.length - || mNavbarColorManagedByIme != navbarColorManagedByIme) { - return false; - } - for (int i = appearanceRegions.length - 1; i >= 0; i--) { - if (!mAppearanceRegions[i].equals(appearanceRegions[i])) { - return false; - } - } - return true; + mBehavior = behavior; + mFullscreen = isFullscreen; } private void showTransient(@InternalInsetsType int[] types) { @@ -1110,14 +1073,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } - private void setFullscreen(boolean isFullscreen) { - mFullscreen = isFullscreen; - } - - private void setImmersive(boolean isImmersive) { - mImmersive = isImmersive; - } - private int getDisabled1() { return mDisabled1; } @@ -1200,7 +1155,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis, state.mImeBackDisposition, state.mShowImeSwitcher, gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken, - state.mNavbarColorManagedByIme, state.mFullscreen, state.mImmersive, + state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen, transientBarTypes); } } diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS new file mode 100644 index 000000000000..a2943f39db24 --- /dev/null +++ b/services/core/java/com/android/server/utils/quota/OWNERS @@ -0,0 +1,4 @@ +dplotnikov@google.com +kwekua@google.com +omakoto@google.com +yamasani@google.com diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4f820845d119..d9bc619fa45f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -42,10 +42,6 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; -import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; @@ -121,7 +117,6 @@ import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; -import android.hardware.input.InputManager; import android.hardware.power.Boost; import android.os.Handler; import android.os.IBinder; @@ -136,16 +131,10 @@ import android.util.Slog; import android.util.SparseArray; import android.view.DisplayCutout; import android.view.Gravity; -import android.view.InputChannel; -import android.view.InputDevice; -import android.view.InputEvent; -import android.view.InputEventReceiver; import android.view.InsetsFlags; import android.view.InsetsSource; import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; -import android.view.MotionEvent; -import android.view.PointerIcon; import android.view.Surface; import android.view.View; import android.view.ViewDebug; @@ -334,7 +323,6 @@ public class DisplayPolicy { // What we last reported to system UI about whether the focused window is fullscreen/immersive. private boolean mLastFocusIsFullscreen = false; - private boolean mLastFocusIsImmersive = false; // If nonzero, a panic gesture was performed at that time in uptime millis and is still pending. private long mPendingPanicGestureUptime; @@ -363,9 +351,6 @@ public class DisplayPolicy { private boolean mDreamingLockscreen; private boolean mAllowLockscreenWhenOn; - @VisibleForTesting - EventReceiverInputConsumer mInputConsumer; - private PointerLocationView mPointerLocationView; /** @@ -1447,49 +1432,6 @@ public class DisplayPolicy { return mForceShowSystemBars; } - /** - * Input handler used while nav bar is hidden. Captures any touch on the screen, - * to determine when the nav bar should be shown and prevent applications from - * receiving those touches. - */ - private final class HideNavInputEventReceiver extends InputEventReceiver { - HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) { - super(inputChannel, looper); - } - - @Override - public void onInputEvent(InputEvent event) { - try { - if (event instanceof MotionEvent - && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - final MotionEvent motionEvent = (MotionEvent) event; - if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { - // When the user taps down, we re-show the nav bar. - boolean changed = false; - synchronized (mLock) { - if (mInputConsumer == null) { - return; - } - showSystemBars(); - } - } - } - } finally { - finishInputEvent(event, false /* handled */); - } - } - - private void showSystemBars() { - final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() - .peekSourceProvider(ITYPE_NAVIGATION_BAR); - final InsetsControlTarget target = - provider != null ? provider.getControlTarget() : null; - if (target != null) { - target.showInsets(Type.systemBars(), false /* fromIme */); - } - } - } - private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames, InsetsState insetsState, WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames, Consumer<Rect> layout) { @@ -1539,48 +1481,10 @@ public class DisplayPolicy { displayFrames.onBeginLayout(mDisplayContent.getInsetsStateController().getRawInsetsState()); mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); - - updateHideNavInputEventReceiver(); - layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */); layoutStatusBar(displayFrames, null /* simulatedContentFrame */); } - void updateHideNavInputEventReceiver() { - final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() - .peekSourceProvider(ITYPE_NAVIGATION_BAR); - final InsetsControlTarget navControlTarget = - provider != null ? provider.getControlTarget() : null; - final WindowState navControllingWin = - navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null; - final boolean navVisible = navControllingWin != null - ? navControllingWin.getRequestedVisibility(ITYPE_NAVIGATION_BAR) - : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); - final boolean showBarsByTouch = navControllingWin != null - && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH; - // 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 - // nav bar and ensure the application doesn't see the event. - if (navVisible || !showBarsByTouch) { - if (mInputConsumer != null) { - mInputConsumer.dismiss(); - mHandler.sendMessage( - mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer)); - mInputConsumer = null; - Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " dismissed."); - } - } else if (mInputConsumer == null && getStatusBar() != null && canHideNavigationBar()) { - mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer( - mHandler.getLooper(), - INPUT_CONSUMER_NAVIGATION, - HideNavInputEventReceiver::new); - Slog.v(TAG, INPUT_CONSUMER_NAVIGATION + " created."); - // As long as mInputConsumer is active, hover events are not dispatched to the app - // and the pointer icon is likely to become stale. Hide it to avoid confusion. - InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL); - } - } - private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) { // decide where the status bar goes ahead of time if (mStatusBar == null) { @@ -2662,8 +2566,6 @@ public class DisplayPolicy { mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance; final int behavior = win.mAttrs.insetsFlags.behavior; - final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE - || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR) || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); if (mLastDisableFlags == disableFlags @@ -2672,7 +2574,6 @@ public class DisplayPolicy { && mLastDockedAppearance == dockedAppearance && mLastBehavior == behavior && mLastFocusIsFullscreen == isFullscreen - && mLastFocusIsImmersive == isImmersive && mLastNonDockedStackBounds.equals(mNonDockedStackBounds) && mLastDockedStackBounds.equals(mDockedStackBounds)) { return false; @@ -2688,7 +2589,6 @@ public class DisplayPolicy { mLastDockedAppearance = dockedAppearance; mLastBehavior = behavior; mLastFocusIsFullscreen = isFullscreen; - mLastFocusIsImmersive = isImmersive; mLastNonDockedStackBounds.set(mNonDockedStackBounds); mLastDockedStackBounds.set(mDockedStackBounds); final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); @@ -2705,9 +2605,8 @@ public class DisplayPolicy { if (statusBar != null) { final int displayId = getDisplayId(); statusBar.setDisableFlags(displayId, disableFlags, cause); - statusBar.onSystemBarAppearanceChanged(displayId, appearance, - appearanceRegions, isNavbarColorManagedByIme); - statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); + statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions, + isNavbarColorManagedByIme, behavior, isFullscreen); } }); @@ -2939,11 +2838,8 @@ public class DisplayPolicy { if (win == null) { return false; } - final int behavior = win.mAttrs.insetsFlags.behavior; return getNavigationBar() != null && canHideNavigationBar() - && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE - || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR) && win != getNotificationShade() && !win.isActivityTypeDream(); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 560547ed1a20..a20d924de058 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER; @@ -53,7 +52,6 @@ import android.graphics.Region; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Process; import android.os.Trace; import android.os.UserHandle; import android.util.ArrayMap; @@ -232,24 +230,6 @@ final class InputMonitor { } } - EventReceiverInputConsumer createInputConsumer(Looper looper, String name, - InputEventReceiver.Factory inputEventReceiverFactory) { - if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { - throw new IllegalArgumentException("Illegal input consumer : " + name - + ", display: " + mDisplayId); - } - - if (mInputConsumers.containsKey(name)) { - throw new IllegalStateException("Existing input consumer found with name: " + name - + ", display: " + mDisplayId); - } - final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService, - this, looper, name, inputEventReceiverFactory, Process.myPid(), - UserHandle.SYSTEM, mDisplayId); - addInputConsumer(name, consumer); - return consumer; - } - void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid, UserHandle clientUser) { if (mInputConsumers.containsKey(name)) { @@ -472,12 +452,10 @@ final class InputMonitor { } private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> { - InputConsumerImpl mNavInputConsumer; InputConsumerImpl mPipInputConsumer; InputConsumerImpl mWallpaperInputConsumer; InputConsumerImpl mRecentsAnimationInputConsumer; - private boolean mAddNavInputConsumerHandle; private boolean mAddPipInputConsumerHandle; private boolean mAddWallpaperInputConsumerHandle; private boolean mAddRecentsAnimationInputConsumerHandle; @@ -487,12 +465,10 @@ final class InputMonitor { private void updateInputWindows(boolean inDrag) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows"); - mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION); mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP); mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER); mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); - mAddNavInputConsumerHandle = mNavInputConsumer != null; mAddPipInputConsumerHandle = mPipInputConsumer != null; mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null; mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null; @@ -557,12 +533,6 @@ final class InputMonitor { } } - if (mAddNavInputConsumerHandle) { - // We set the layer to z=MAX-1 so that it's always on top. - mNavInputConsumer.show(mInputTransaction, Integer.MAX_VALUE - 1); - mAddNavInputConsumerHandle = false; - } - if (mAddWallpaperInputConsumerHandle) { if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisible()) { // Add the wallpaper input consumer above the first visible wallpaper. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index ee150c31184c..94a7ebd37760 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -142,7 +142,6 @@ class InsetsPolicy { getFakeControlTarget(focusedWin, navControlTarget)); mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR); mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR); - mPolicy.updateHideNavInputEventReceiver(); } boolean isHidden(@InternalInsetsType int type) { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index ff2509b9ffc7..b1606c506d5b 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY; +import static android.Manifest.permission.USE_BACKGROUND_BLUR; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; @@ -108,6 +109,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final boolean mCanCreateSystemApplicationOverlay; final boolean mCanHideNonSystemOverlayWindows; final boolean mCanAcquireSleepToken; + final boolean mCanUseBackgroundBlur; private AlertWindowNotification mAlertWindowNotification; private boolean mShowingAlertWindowNotificationAllowed; private boolean mClientDead = false; @@ -136,6 +138,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { && !mService.mAtmInternal.isCallerRecents(mUid); mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER) == PERMISSION_GRANTED; + mCanUseBackgroundBlur = service.mContext.checkCallingOrSelfPermission(USE_BACKGROUND_BLUR) + == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; mDragDropController = mService.mDragDropController; StringBuilder sb = new StringBuilder(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 042b7e36428b..855c8f562dda 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3329,6 +3329,14 @@ class Task extends WindowContainer<WindowContainer> { return false; } + @Override + boolean handlesOrientationChangeFromDescendant() { + return super.handlesOrientationChangeFromDescendant() + // Display won't rotate for the orientation request if the TaskDisplayArea can't + // specify orientation. + && getDisplayArea().canSpecifyOrientation(); + } + void resize(boolean relayout, boolean forced) { if (setBounds(getRequestedOverrideBounds(), forced) != BOUNDS_CHANGE_NONE && relayout) { getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index bc0235c0e934..0136c010429b 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1902,7 +1902,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } /** Whether this task display area can request orientation. */ - @VisibleForTesting boolean canSpecifyOrientation() { // Only allow to specify orientation if this TDA is not set to ignore orientation request, // and it is the last focused one on this logical display that can request orientation diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a034bac993e0..4156ed602464 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1620,7 +1620,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId, - session.mCanAddInternalSystemWindow); + session.mCanAddInternalSystemWindow, session.mCanUseBackgroundBlur); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 13b976524bb9..b7602fec73df 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -45,6 +45,7 @@ import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; @@ -297,6 +298,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int mShowUserId; /** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */ final boolean mOwnerCanAddInternalSystemWindow; + /** The owner has {@link android.Manifest.permission#USE_BACKGROUND_BLUR} */ + final boolean mOwnerCanUseBackgroundBlur; final WindowId mWindowId; WindowToken mToken; // The same object as mToken if this is an app window and null for non-app windows. @@ -854,9 +857,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, - int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) { + int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow, + boolean ownerCanUseBackgroundBlur) { this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId, - ownerCanAddInternalSystemWindow, new PowerManagerWrapper() { + ownerCanAddInternalSystemWindow, ownerCanUseBackgroundBlur, + new PowerManagerWrapper() { @Override public void wakeUp(long time, @WakeReason int reason, String details) { service.mPowerManager.wakeUp(time, reason, details); @@ -872,7 +877,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility, int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow, - PowerManagerWrapper powerManagerWrapper) { + boolean ownerCanUseBackgroundBlur, PowerManagerWrapper powerManagerWrapper) { super(service); mTmpTransaction = service.mTransactionFactory.get(); mSession = s; @@ -883,6 +888,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mOwnerUid = ownerId; mShowUserId = showUserId; mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow; + mOwnerCanUseBackgroundBlur = ownerCanUseBackgroundBlur; mWindowId = new WindowId(this); mAttrs.copyFrom(a); mLastSurfaceInsets.set(mAttrs.surfaceInsets); @@ -5245,7 +5251,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (!mAnimatingExit && mAppDied) { mIsDimming = true; getDimmer().dimAbove(getSyncTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW); - } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || mAttrs.backgroundBlurRadius != 0) + } else if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || isBlurEnabled()) && isVisibleNow() && !mHidden) { // Only show the Dimmer when the following is satisfied: // 1. The window has the flag FLAG_DIM_BEHIND or background blur is requested @@ -5254,11 +5260,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 4. The WS is not hidden. mIsDimming = true; final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0; - getDimmer().dimBelow( - getSyncTransaction(), this, mAttrs.dimAmount, mAttrs.backgroundBlurRadius); + final int blurRadius = isBlurEnabled() ? mAttrs.backgroundBlurRadius : 0; + getDimmer().dimBelow(getSyncTransaction(), this, dimAmount, blurRadius); } } + private boolean isBlurEnabled() { + return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur; + } + /** * Notifies SF about the priority of the window, if it changed. SF then uses this information diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS new file mode 100644 index 000000000000..2e9e62570653 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/utils/quota/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 74c6a7e36c9d..784718b5f51e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -53,7 +53,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IUserSwitchObserver; @@ -71,6 +73,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -161,7 +164,7 @@ public class UserControllerTest { // All UserController params are set to default. mUserController = new UserController(mInjector); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); - setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true); + setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true, null); }); } @@ -549,19 +552,75 @@ public class UserControllerTest { /* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true); } + @Test + public void testStartProfile_fullUserFails() { + setUpUser(TEST_USER_ID1, 0); + assertThrows(IllegalArgumentException.class, + () -> mUserController.startProfile(TEST_USER_ID1)); + } + + @Test + public void testStopProfile_fullUserFails() throws Exception { + setUpAndStartUserInBackground(TEST_USER_ID1); + assertThrows(IllegalArgumentException.class, + () -> mUserController.stopProfile(TEST_USER_ID1)); + } + + @Test + public void testStartProfile_disabledProfileFails() { + setUpUser(TEST_USER_ID1, UserInfo.FLAG_PROFILE | UserInfo.FLAG_DISABLED, /* preCreated= */ + false, UserManager.USER_TYPE_PROFILE_MANAGED); + assertThat(mUserController.startProfile(TEST_USER_ID1)).isFalse(); + } + + @Test + public void testStartProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1); + startBackgroundUserAssertions(); + } + + @Test + public void testStopProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); + } + private void setUpAndStartUserInBackground(int userId) throws Exception { setUpUser(userId, 0); mUserController.startUser(userId, /* foreground= */ false); verify(mInjector.mStorageManagerMock, times(1)) - .unlockUserKey(TEST_USER_ID, 0, null, null); + .unlockUserKey(userId, /* serialNumber= */ 0, /* token= */ null, /* secret= */ + null); + mUserStates.put(userId, mUserController.getStartedUserState(userId)); + } + + private void setUpAndStartProfileInBackground(int userId) throws Exception { + setUpUser(userId, UserInfo.FLAG_PROFILE, false, UserManager.USER_TYPE_PROFILE_MANAGED); + assertThat(mUserController.startProfile(userId)).isTrue(); + + verify(mInjector.mStorageManagerMock, times(1)) + .unlockUserKey(userId, /* serialNumber= */ 0, /* token= */ null, /* secret= */ + null); mUserStates.put(userId, mUserController.getStartedUserState(userId)); } private void assertUserLockedOrUnlockedAfterStopping(int userId, boolean delayedLocking, - KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception { + KeyEvictedCallback keyEvictedCallback, boolean expectLocking) throws Exception { int r = mUserController.stopUser(userId, /* force= */ true, /* delayedLocking= */ delayedLocking, null, keyEvictedCallback); assertThat(r).isEqualTo(ActivityManager.USER_OP_SUCCESS); + assertUserLockedOrUnlockedState(userId, delayedLocking, expectLocking); + } + + private void assertProfileLockedOrUnlockedAfterStopping(int userId, boolean expectLocking) + throws Exception { + boolean profileStopped = mUserController.stopProfile(userId); + assertThat(profileStopped).isTrue(); + assertUserLockedOrUnlockedState(userId, /* delayedLocking= */ false, expectLocking); + } + + private void assertUserLockedOrUnlockedState(int userId, boolean delayedLocking, + boolean expectLocking) throws InterruptedException, RemoteException { // fake all interim steps UserState ussUser = mUserStates.get(userId); ussUser.setState(UserState.STATE_SHUTDOWN); @@ -594,11 +653,16 @@ public class UserControllerTest { } private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) { - setUpUser(userId, flags, /* preCreated= */ false); + setUpUser(userId, flags, /* preCreated= */ false, /* userType */ null); } - private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) { - UserInfo userInfo = new UserInfo(userId, "User" + userId, flags); + private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated, + @Nullable String userType) { + if (userType == null) { + userType = UserInfo.getDefaultUserType(flags); + } + UserInfo userInfo = new UserInfo(userId, "User" + userId, /* iconPath= */ null, flags, + userType); userInfo.preCreated = preCreated; when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated); diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 5f654406812f..a5039fb5fecd 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -82,6 +82,7 @@ public class PowerStatsServiceTest { private PowerStatsLogger mPowerStatsLogger; private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() { + private TestPowerStatsHALWrapper mTestPowerStatsHALWrapper = new TestPowerStatsHALWrapper(); @Override File createDataStoragePath() { mDataStorageDir = null; @@ -111,8 +112,8 @@ public class PowerStatsServiceTest { } @Override - IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { - return new TestPowerStatsHALWrapper(); + IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() { + return mTestPowerStatsHALWrapper; } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 6f5a874114ea..5c67db7ef813 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -514,7 +514,7 @@ public class DisplayAreaTest extends WindowTestsBase { return new WindowState(mWm, mock(Session.class), new TestIWindow(), token, null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(), View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */, - false /* ownerCanAddInternalSystemWindow */); + false /* ownerCanAddInternalSystemWindow */, false /* ownerCanUseBackgroundBlur */); } private WindowToken createWindowToken(int type) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index c6925899a223..22ee7e4f229f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -22,8 +22,6 @@ import static android.view.Surface.ROTATION_0; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE; -import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -32,13 +30,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; @@ -47,7 +43,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -293,46 +288,6 @@ public class DisplayPolicyTests extends WindowTestsBase { return win; } - @UseTestDisplay( - addWindows = { W_ACTIVITY, W_STATUS_BAR, W_NAVIGATION_BAR, W_NOTIFICATION_SHADE }) - @Test - public void testUpdateHideNavInputEventReceiver() { - final InsetsPolicy insetsPolicy = mDisplayContent.getInsetsPolicy(); - final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); - displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs); - displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); - displayPolicy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs); - spyOn(displayPolicy); - doReturn(true).when(displayPolicy).hasNavigationBar(); - - // App doesn't request to hide navigation bar. - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - - // App requests to hide navigation bar. - final InsetsState requestedState = new InsetsState(); - requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false); - mAppWindow.updateRequestedVisibility(requestedState); - insetsPolicy.onInsetsModified(mAppWindow); - assertNotNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar, but without BEHAVIOR_SHOW_BARS_BY_TOUCH. - mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar, but with BEHAVIOR_SHOW_BARS_BY_TOUCH. - mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNotNull(displayPolicy.mInputConsumer); - - // App still requests to hide navigation bar with BEHAVIOR_SHOW_BARS_BY_TOUCH, - // but notification shade forcibly shows navigation bar - mNotificationShadeWindow.mAttrs.privateFlags |= PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; - insetsPolicy.updateBarControlTarget(mAppWindow); - assertNull(displayPolicy.mInputConsumer); - } - @UseTestDisplay(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD }) @Test public void testImeMinimalSourceFrame() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 61911b3c36be..e6f24da3e7b9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -33,6 +33,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.SizeCompatTests.prepareLimitedBounds; import static com.android.server.wm.SizeCompatTests.prepareUnresizable; import static com.android.server.wm.SizeCompatTests.rotateDisplay; @@ -309,6 +310,43 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(mSecondRoot.findAreaForToken(imeToken)).isEqualTo(imeContainer); } + @Test + public void testResizableFixedOrientationApp_taskLevelLetterboxing() { + mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); + + // Launch portrait on first DAG + mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); + prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, + false /* isUnresizable */); + + // Display in landscape (as opposite to DAG), first DAG and activity in portrait + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + // Launch portrait on second DAG + mDisplay.onLastFocusedTaskDisplayAreaChanged(mSecondTda); + prepareLimitedBounds(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE, + false /* isUnresizable */); + + // Display in portrait (as opposite to DAG), first DAG and activity in landscape + assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mSecondTask.isTaskLetterboxed()).isFalse(); + assertThat(mSecondActivity.inSizeCompatMode()).isFalse(); + + // First activity is letterboxed in portrait as requested. + assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); + assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); + assertThat(mFirstTask.isTaskLetterboxed()).isTrue(); + assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); + + } + private void setupImeWindow() { final WindowState imeWindow = createWindow(null /* parent */, TYPE_INPUT_METHOD, mDisplay, "mImeWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 22430a110ca3..eb44476a8618 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; @@ -505,7 +506,7 @@ public class SizeCompatTests extends WindowTestsBase { compatTokens.clear(); // Make the activity resizable again by restarting it - activity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + activity.info.resizeMode = RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; activity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed @@ -522,7 +523,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2500); // Make the task root resizable. - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE; // Create a size compat activity on the same task. final ActivityRecord activity = new ActivityBuilder(mAtm) @@ -550,7 +551,7 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(1000, 2500); // Make the task root resizable. - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; + mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE; // Create a size compat activity on the same task. final ActivityRecord activity = new ActivityBuilder(mAtm) @@ -946,13 +947,25 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation); } - /** - * Setups {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or - * orientation. - */ static void prepareUnresizable(ActivityRecord activity, float maxAspect, int screenOrientation) { - activity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + prepareLimitedBounds(activity, maxAspect, screenOrientation, true /* isUnresizable */); + } + + static void prepareLimitedBounds(ActivityRecord activity, int screenOrientation, + boolean isUnresizable) { + prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable); + } + + /** + * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed + * orientation, and/or whether it is resizable. + */ + static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, + int screenOrientation, boolean isUnresizable) { + activity.info.resizeMode = isUnresizable + ? RESIZE_MODE_UNRESIZEABLE + : RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; if (maxAspect >= 0) { activity.info.maxAspectRatio = maxAspect; 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 8b93372e5a76..c85991d8f3b2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -340,7 +340,7 @@ class WindowTestsBase extends SystemServiceTestsBase { final WindowState w = new WindowState(service, session, iWindow, token, parent, OP_NONE, attrs, VISIBLE, ownerId, userId, - ownerCanAddInternalSystemWindow, + ownerCanAddInternalSystemWindow, false /* ownerCanUseBackgroundBlur */, powerManagerWrapper); // TODO: Probably better to make this call in the WindowState ctor to avoid errors with // adding it to the token... @@ -1213,7 +1213,8 @@ class WindowTestsBase extends SystemServiceTestsBase { TestWindowState(WindowManagerService service, Session session, IWindow window, WindowManager.LayoutParams attrs, WindowToken token) { super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0, - false /* ownerCanAddInternalSystemWindow */); + false /* ownerCanAddInternalSystemWindow */, + false /* ownerCanUseBackgroundBlur */); } @Override diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index e2aabe6a89ea..84c6e7be16be 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -22,13 +22,17 @@ import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; import android.content.Context; import android.os.RemoteException; import android.util.Slog; +import android.view.autofill.AutofillId; import android.view.translation.ITranslationManager; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationManager.UiTranslationState; import com.android.internal.os.IResultReceiver; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; +import java.util.List; + /** * Entry point service for translation management. * @@ -82,6 +86,19 @@ public final class TranslationManagerService } } } + + @Override + public void updateUiTranslationState(@UiTranslationState int state, + TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds, + int taskId, int userId) { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.updateUiTranslationState(state, sourceSpec, destSpec, viewIds, + taskId); + } + } + } } @Override // from SystemService diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index b1f6f80d4158..5b1074fc4c0a 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -26,7 +26,9 @@ import android.content.pm.ServiceInfo; import android.os.RemoteException; import android.service.translation.TranslationServiceInfo; import android.util.Slog; +import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationManager.UiTranslationState; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -34,6 +36,7 @@ import com.android.internal.util.SyncResultReceiver; import com.android.server.infra.AbstractPerUserSystemService; import java.util.ArrayList; +import java.util.List; final class TranslationManagerServiceImpl extends AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { @@ -122,4 +125,11 @@ final class TranslationManagerServiceImpl extends remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver); } } + + @GuardedBy("mLock") + public void updateUiTranslationState(@UiTranslationState int state, + TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds, + int taskId) { + // TODO: implement this in next change + } } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 04b365f4a5c7..717040adfac9 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -18,6 +18,7 @@ package android.telecom; import static android.Manifest.permission.MODIFY_PHONE_STATE; +import android.Manifest; import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.IntRange; @@ -3357,6 +3358,24 @@ public abstract class Connection extends Conferenceable { */ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} + /** + * Indicates that call filtering in Telecom is complete + * + * This method is called for a connection created via + * {@link ConnectionService#onCreateIncomingConnection} when call filtering completes in + * Telecom, including checking the blocked number db, per-contact settings, and custom call + * filtering apps. + * + * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is + * {@code true}, {@link #onDisconnect()} will be called soon after + * this is called. + * @param isInContacts Indicates whether the caller is in the user's contacts list. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_CONTACTS) + public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { } + static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index b1ccb533e83d..f86f9d5dad3d 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -147,6 +147,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; + private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC"; private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; private static final String SESSION_START_RTT = "CS.+RTT"; @@ -200,6 +201,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_ADD_PARTICIPANT = 39; private static final int MSG_EXPLICIT_CALL_TRANSFER = 40; private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41; + private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42; private static Connection sNullConnection; @@ -722,6 +724,22 @@ public abstract class ConnectionService extends Service { } @Override + public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = isBlocked; + args.arg3 = isInContacts; + args.arg4 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED); try { @@ -1354,6 +1372,21 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_ON_CALL_FILTERING_COMPLETED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + Log.continueSession((Session) args.arg4, + SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED); + String callId = (String) args.arg1; + boolean isBlocked = (boolean) args.arg2; + boolean isInContacts = (boolean) args.arg3; + onCallFilteringCompleted(callId, isBlocked, isInContacts); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_HANDOVER_COMPLETE: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -2345,6 +2378,14 @@ public abstract class ConnectionService extends Service { } } + private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) { + Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts); + Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted"); + if (connection != null) { + connection.onCallFilteringCompleted(isBlocked, isInContacts); + } + } + /** * Notifies a {@link Connection} that a handover has completed. * diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 52210a55c8d0..feb2ca53bbbe 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -16,8 +16,10 @@ package android.telecom; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -1198,6 +1200,28 @@ public final class RemoteConnection { } /** + * Notifies this {@link RemoteConnection} that call filtering has completed, as well as + * the results of a contacts lookup for the remote party. + * @param isBlocked Whether call filtering indicates that the call should be blocked + * @param isInContacts Whether the remote party is in the user's contacts + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_CONTACTS) + public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { + Log.startSession("RC.oCFC", getActiveOwnerInfo()); + try { + if (mConnected) { + mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts, + null /*Session.Info*/); + } + } catch (RemoteException ignored) { + } finally { + Log.endSession(); + } + } + + /** * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT * upgrade request sent via {@link Connection#sendRemoteRttRequest}. * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null, diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index fb5417994b57..92264be5fa90 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -118,6 +118,9 @@ oneway interface IConnectionService { void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo); + void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, + in Session.Info sessionInfo); + void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo); void startRtt(String callId, in ParcelFileDescriptor fromInCall, diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml index 1a236c6f751d..d6355f5a0464 100644 --- a/tests/WindowInsetsTests/res/values/strings.xml +++ b/tests/WindowInsetsTests/res/values/strings.xml @@ -23,7 +23,7 @@ <!-- The item positions should match the flag values respectively. --> <string-array name="behaviors"> <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item> - <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item> + <item>BEHAVIOR_DEFAULT</item> <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item> </string-array> </resources> diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java index beb4049cb230..95fd959e5587 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java @@ -53,6 +53,8 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn R.array.behaviors, android.R.layout.simple_spinner_item); adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerBehavior.setAdapter(adapterBehavior); + spinnerBehavior.setSelection( + spinnerBehavior.getWindowInsetsController().getSystemBarsBehavior()); spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 3c08d347b19a..c04ddd78e69b 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -16,6 +16,7 @@ android_test { "frameworks-base-testutils", "framework-protos", "mockito-target-minus-junit4", + "net-tests-utils", "platform-test-annotations", "services.core", ], diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java new file mode 100644 index 000000000000..9c6b7194af35 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -0,0 +1,106 @@ +/* + * 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 android.net.vcn; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.Executor; + +public class VcnManagerTest { + private static final Executor INLINE_EXECUTOR = Runnable::run; + + private IVcnManagementService mMockVcnManagementService; + private VcnUnderlyingNetworkPolicyListener mMockPolicyListener; + + private Context mContext; + private VcnManager mVcnManager; + + @Before + public void setUp() { + mMockVcnManagementService = mock(IVcnManagementService.class); + mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class); + + mContext = getContext(); + mVcnManager = new VcnManager(mContext, mMockVcnManagementService); + } + + @Test + public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor = + ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class); + verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture()); + + assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + + IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue(); + listenerWrapper.onPolicyChanged(); + verify(mMockPolicyListener).onPolicyChanged(); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService, never()) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null); + } + + @Test(expected = NullPointerException.class) + public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java new file mode 100644 index 000000000000..3ba0a1f53a9f --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -0,0 +1,51 @@ +/* + * 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 android.net.vcn; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.net.NetworkCapabilities; + +import org.junit.Test; + +public class VcnUnderlyingNetworkPolicyTest { + private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + false /* isTearDownRequested */, new NetworkCapabilities()); + private static final VcnUnderlyingNetworkPolicy SAMPLE_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + true /* isTearDownRequested */, + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build()); + + @Test + public void testEquals() { + assertEquals(DEFAULT_NETWORK_POLICY, DEFAULT_NETWORK_POLICY); + assertEquals(SAMPLE_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + + assertNotEquals(DEFAULT_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(SAMPLE_NETWORK_POLICY, 2); + } +} |