summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Android.bp1
-rw-r--r--apct-tests/perftests/OWNERS11
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java15
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java45
-rw-r--r--core/api/current.txt20
-rw-r--r--core/api/system-current.txt34
-rw-r--r--core/java/android/accessibilityservice/OWNERS2
-rw-r--r--core/java/android/app/ActivityManager.java46
-rw-r--r--core/java/android/app/ActivityManagerInternal.java13
-rw-r--r--core/java/android/app/AppOpsManager.java2
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java3
-rw-r--r--core/java/android/app/IActivityManager.aidl18
-rw-r--r--core/java/android/app/SystemServiceRegistry.java14
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/Intent.java24
-rw-r--r--core/java/android/content/pm/AppSearchPerson.java159
-rw-r--r--core/java/android/content/pm/AppSearchShortcutInfo.java616
-rw-r--r--core/java/android/content/pm/IPackageInstallerSession.aidl2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java12
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java2
-rw-r--r--core/java/android/net/vcn/IVcnManagementService.aidl4
-rw-r--r--core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl22
-rw-r--r--core/java/android/net/vcn/VcnManager.java107
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl20
-rw-r--r--core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java110
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java11
-rw-r--r--core/java/android/text/FontConfig.java330
-rw-r--r--core/java/android/view/InsetsFlags.java10
-rw-r--r--core/java/android/view/PendingInsetsController.java3
-rw-r--r--core/java/android/view/View.java22
-rw-r--r--core/java/android/view/ViewRootImpl.java39
-rw-r--r--core/java/android/view/WindowInsetsController.java29
-rw-r--r--core/java/android/view/WindowManager.java16
-rw-r--r--core/java/android/view/accessibility/OWNERS1
-rw-r--r--core/java/android/view/translation/ITranslationManager.aidl9
-rw-r--r--core/java/android/view/translation/UiTranslationManager.java179
-rw-r--r--core/java/android/widget/AbsSeekBar.java21
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java2
-rw-r--r--core/java/com/android/internal/app/ChooserActivityLogger.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java82
-rw-r--r--core/java/com/android/internal/os/WrapperInit.java5
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java10
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java38
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl10
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java14
-rw-r--r--core/jni/android_os_HwBlob.cpp12
-rw-r--r--core/proto/android/server/activitymanagerservice.proto1
-rw-r--r--core/res/AndroidManifest.xml17
-rw-r--r--core/res/res/values/attrs.xml16
-rw-r--r--core/res/res/values/attrs_manifest.xml6
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java47
-rw-r--r--core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java69
-rw-r--r--core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java134
-rw-r--r--core/tests/coretests/src/android/graphics/TypefaceTest.java10
-rw-r--r--core/tests/coretests/src/android/text/FontFallbackSetup.java23
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java28
-rw-r--r--core/tests/coretests/src/android/widget/AbsSeekBarTest.java49
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java83
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java6
-rw-r--r--data/etc/car/com.android.car.shell.xml4
-rw-r--r--graphics/java/android/graphics/FontListParser.java137
-rw-r--r--graphics/java/android/graphics/Typeface.java34
-rw-r--r--graphics/java/android/graphics/drawable/BackgroundBlurDrawable.java (renamed from core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java)107
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java18
-rw-r--r--graphics/java/android/graphics/fonts/FontCustomizationParser.java81
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java26
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java223
-rw-r--r--keystore/java/android/security/AuthTokenUtils.java75
-rw-r--r--keystore/java/android/security/Authorization.java78
-rw-r--r--keystore/java/android/security/KeyStore.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java9
-rw-r--r--libs/hwui/RecordingCanvas.cpp73
-rw-r--r--libs/hwui/SkiaCanvas.h3
-rw-r--r--libs/hwui/hwui/Canvas.h3
-rw-r--r--libs/hwui/jni/android_graphics_DisplayListCanvas.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp8
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h5
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.cpp2
-rw-r--r--libs/hwui/tests/common/TestUtils.h14
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/TvApp.cpp6
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp39
-rw-r--r--libs/hwui/tests/microbench/RenderNodeBench.cpp17
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp5
-rw-r--r--media/java/android/media/metrics/IPlaybackMetricsManager.aidl2
-rw-r--r--media/java/android/media/metrics/PlaybackMetricsManager.java12
-rw-r--r--media/java/android/media/metrics/PlaybackSession.java7
-rw-r--r--media/java/android/media/metrics/TrackChangeEvent.aidl19
-rw-r--r--media/java/android/media/metrics/TrackChangeEvent.java480
-rw-r--r--packages/SettingsLib/SettingsSpinner/Android.bp4
-rw-r--r--packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml31
-rw-r--r--packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java128
-rw-r--r--packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinnerAdapter.java29
-rw-r--r--packages/SettingsLib/tests/integ/Android.bp1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java92
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java2
-rw-r--r--packages/Shell/Android.bp23
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml5
-rw-r--r--packages/SystemUI/res/drawable/qs_media_background.xml2
-rw-r--r--packages/SystemUI/res/layout/media_view.xml69
-rw-r--r--packages/SystemUI/res/values-night/colors.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/flags.xml26
-rw-r--r--packages/SystemUI/res/xml/media_collapsed.xml91
-rw-r--r--packages/SystemUI/res/xml/media_expanded.xml67
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wrapper/BuildInfo.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java175
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java11
-rw-r--r--services/accessibility/OWNERS2
-rw-r--r--services/core/java/android/power/PowerStatsInternal.java42
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java6
-rw-r--r--services/core/java/com/android/server/OWNERS3
-rw-r--r--services/core/java/com/android/server/PinnerService.java206
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java17
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java34
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java42
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java139
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java82
-rw-r--r--services/core/java/com/android/server/am/FgsStartTempAllowList.java5
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java5
-rw-r--r--services/core/java/com/android/server/am/UserController.java145
-rw-r--r--services/core/java/com/android/server/appop/HistoricalRegistry.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java7
-rw-r--r--services/core/java/com/android/server/graphics/fonts/FontManagerService.java113
-rw-r--r--services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java26
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java148
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java69
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java1
-rw-r--r--services/core/java/com/android/server/powerstats/PowerStatsService.java65
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java75
-rw-r--r--services/core/java/com/android/server/utils/quota/OWNERS4
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java108
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java30
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/Session.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java22
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java76
-rw-r--r--services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java5
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerService.java17
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java10
-rw-r--r--telecomm/java/android/telecom/Connection.java19
-rwxr-xr-xtelecomm/java/android/telecom/ConnectionService.java41
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java24
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl3
-rw-r--r--tests/WindowInsetsTests/res/values/strings.xml2
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java2
-rw-r--r--tests/vcn/Android.bp1
-rw-r--r--tests/vcn/java/android/net/vcn/VcnManagerTest.java106
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java51
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);
+ }
+}