[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into stage-aosp-master am: 1618dd33c9 -s ours am: 1d0cabae5a -s ours am: 243aafc61e -s ours am: 98f03b36f0 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21605061
Change-Id: I5994946db99a6a07323afd9da2c2868c283c90b6
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
index 8fb057d..6abab6e 100644
--- a/apct-tests/perftests/core/OWNERS
+++ b/apct-tests/perftests/core/OWNERS
@@ -3,4 +3,12 @@
# Bug component: 568761
per-file /apct-tests/perftests/core/res/* = [email protected],[email protected]
+per-file /apct-tests/perftests/core/res/* = file:/core/java/android/content/om/OWNERS
+per-file /apct-tests/perftests/core/src/android/content/res/* = [email protected]
+per-file /apct-tests/perftests/core/src/android/content/res/* = file:/core/java/android/content/res/OWNERS
+
+
+# Bug component: 568631
+per-file /apct-tests/perftests/core/src/android/content/om/* = [email protected]
+per-file /apct-tests/perftests/core/src/android/content/om/* = file:/core/java/android/content/om/OWNERS
diff --git a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
new file mode 100644
index 0000000..13269eb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 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.input
+
+import android.perftests.utils.PerfStatusReporter
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.VelocityTracker
+
+import androidx.test.filters.LargeTest
+import androidx.test.runner.AndroidJUnit4
+
+import java.time.Duration
+
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Helper class to maintain [MotionEvent]s for tests.
+ *
+ * This is primarily used to create [MotionEvent]s for tests, in a way where a sequence of
+ * [MotionEvent]s created in multiple test runs are exactly the same, as long as [reset] is called
+ * between consecutive sequences of [MotionEvent]s.
+ *
+ * Furthermore, it also contains convenience methods to run any queries/verifications of the
+ * generated [MotionEvent]s.
+ */
+abstract class MotionState {
+ /** Current time, in ms. */
+ protected var currentTime = START_TIME
+
+ /** Resets the state of this instance. */
+ open fun reset() {
+ currentTime = START_TIME
+ }
+
+ /** Creates a [MotionEvent]. */
+ abstract fun createMotionEvent(): MotionEvent
+
+ /** Asserts that the current velocity is not zero, just for verifying there's motion. */
+ abstract fun assertNonZeroVelocity(velocityTracker: VelocityTracker)
+
+ companion object {
+ /** Arbitrarily chosen start time. */
+ val START_TIME = Duration.ofMillis(100)
+ /**
+ * A small enough time jump, which won't be considered by the tracker as big enough to
+ * deduce that a pointer has stopped.
+ */
+ val DEFAULT_TIME_JUMP = Duration.ofMillis(2)
+ }
+}
+
+/** An implementation of [MotionState] for [MotionEvent.AXIS_SCROLL]. */
+private class ScrollMotionState : MotionState() {
+ override fun createMotionEvent(): MotionEvent {
+ val props = MotionEvent.PointerProperties()
+ props.id = 0
+ val coords = MotionEvent.PointerCoords()
+ coords.setAxisValue(MotionEvent.AXIS_SCROLL, DEFAULT_SCROLL_AMOUNT)
+ val motionEvent = MotionEvent.obtain(
+ /*downTime=*/0,
+ currentTime.toMillis(),
+ MotionEvent.ACTION_SCROLL,
+ /*pointerCount=*/1,
+ arrayOf(props),
+ arrayOf(coords),
+ /*metaState=*/0,
+ /*buttonState=*/0,
+ /*xPrecision=*/0f,
+ /*yPrecision=*/0f,
+ /*deviceId=*/1,
+ /*edgeFlags=*/0,
+ InputDevice.SOURCE_ROTARY_ENCODER,
+ /*flags=*/0
+ )
+
+ currentTime = currentTime.plus(DEFAULT_TIME_JUMP)
+
+ return motionEvent
+ }
+
+ override fun assertNonZeroVelocity(velocityTracker: VelocityTracker) {
+ Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) != 0f)
+ }
+
+ companion object {
+ private val DEFAULT_SCROLL_AMOUNT: Float = 30f
+ }
+}
+
+/** An implementation of [MotionState] for [MotionEvent.AXIS_X] and [MotionEvent.AXIS_Y]. */
+private class PlanarMotionState : MotionState() {
+ private var x: Float = DEFAULT_X
+ private var y: Float = DEFAULT_Y
+ private var downEventCreated = false
+
+ override fun createMotionEvent(): MotionEvent {
+ val action: Int = if (downEventCreated) MotionEvent.ACTION_MOVE else MotionEvent.ACTION_DOWN
+ val motionEvent = MotionEvent.obtain(
+ /*downTime=*/START_TIME.toMillis(),
+ currentTime.toMillis(),
+ action,
+ x,
+ y,
+ /*metaState=*/0)
+
+ if (downEventCreated) {
+ x += INCREMENT
+ y += INCREMENT
+ } else {
+ downEventCreated = true
+ }
+ currentTime = currentTime.plus(DEFAULT_TIME_JUMP)
+
+ return motionEvent
+ }
+
+ override fun assertNonZeroVelocity(velocityTracker: VelocityTracker) {
+ Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_X) != 0f)
+ Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_Y) != 0f)
+ }
+
+ override fun reset() {
+ super.reset()
+ x = DEFAULT_X
+ y = DEFAULT_Y
+ downEventCreated = false
+ }
+
+ companion object {
+ /** Arbitrarily chosen constants. No need to have varying velocity for now. */
+ private val DEFAULT_X: Float = 2f
+ private val DEFAULT_Y: Float = 4f
+ private val INCREMENT: Float = 0.7f
+ }
+}
+
+/**
+ * Benchmark tests for [VelocityTracker]
+ *
+ * Build/Install/Run:
+ * atest VelocityTrackerBenchmarkTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class VelocityTrackerBenchmarkTest {
+ @get:Rule
+ val perfStatusReporter: PerfStatusReporter = PerfStatusReporter()
+
+ private val velocityTracker = VelocityTracker.obtain()
+ @Before
+ fun setup() {
+ velocityTracker.clear()
+ }
+
+ @Test
+ fun addMovement_axisScroll() {
+ testAddMovement(ScrollMotionState())
+ }
+
+ @Test
+ fun computeCurrentVelocity_computeAfterAllAdditions_axisScroll() {
+ testComputeCurrentVelocity_computeAfterAllAdditions(ScrollMotionState())
+ }
+
+ @Test
+ fun computeCurrentVelocity_computeAfterEachAdd_axisScroll() {
+ testComputeCurrentVelocity_computeAfterEachAdd(ScrollMotionState())
+ }
+
+ @Test
+ fun addMovementTest_planarAxes() {
+ testAddMovement(PlanarMotionState())
+ }
+
+ @Test
+ fun computeCurrentVelocity_computeAfterAllAdditions_planarAxes() {
+ testComputeCurrentVelocity_computeAfterAllAdditions(PlanarMotionState())
+ }
+
+ private fun testAddMovement(motionState: MotionState) {
+ val state = perfStatusReporter.getBenchmarkState()
+ while (state.keepRunning()) {
+ state.pauseTiming()
+ for (i in 0 until TEST_NUM_DATAPOINTS) {
+ val motionEvent = motionState.createMotionEvent()
+ state.resumeTiming()
+ velocityTracker.addMovement(motionEvent)
+ state.pauseTiming()
+ }
+ velocityTracker.computeCurrentVelocity(1000)
+ motionState.assertNonZeroVelocity(velocityTracker)
+ // Clear the tracker for the next run
+ velocityTracker.clear()
+ motionState.reset()
+ state.resumeTiming()
+ }
+ }
+
+ private fun testComputeCurrentVelocity_computeAfterAllAdditions(motionState: MotionState) {
+ val state = perfStatusReporter.getBenchmarkState()
+ while (state.keepRunning()) {
+ // Add the data points
+ state.pauseTiming()
+ for (i in 0 until TEST_NUM_DATAPOINTS) {
+ velocityTracker.addMovement(motionState.createMotionEvent())
+ }
+
+ // Do the velocity computation
+ state.resumeTiming()
+ velocityTracker.computeCurrentVelocity(1000)
+
+ state.pauseTiming()
+ motionState.assertNonZeroVelocity(velocityTracker)
+ // Clear the tracker for the next run
+ velocityTracker.clear()
+ state.resumeTiming()
+ }
+ }
+
+ private fun testComputeCurrentVelocity_computeAfterEachAdd(motionState: MotionState) {
+ val state = perfStatusReporter.getBenchmarkState()
+ while (state.keepRunning()) {
+ state.pauseTiming()
+ for (i in 0 until TEST_NUM_DATAPOINTS) {
+ velocityTracker.addMovement(motionState.createMotionEvent())
+ state.resumeTiming()
+ velocityTracker.computeCurrentVelocity(1000)
+ state.pauseTiming()
+ }
+ motionState.assertNonZeroVelocity(velocityTracker)
+ // Clear the tracker for the next run
+ velocityTracker.clear()
+ state.resumeTiming()
+ }
+ }
+
+ companion object {
+ private const val TEST_NUM_DATAPOINTS = 100
+ }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 051dde0..4c2cf2b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -1554,8 +1554,9 @@
}
private void waitCoolDownPeriod() {
- final int tenSeconds = 1000 * 10;
+ // Heuristic value based on local tests. Stability increased compared to no waiting.
+ final int fiveSeconds = 1000 * 5;
waitForBroadcastIdle();
- sleep(tenSeconds);
+ sleep(fiveSeconds);
}
}
diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp
index 5ee5824..ee31aed 100644
--- a/cmds/locksettings/Android.bp
+++ b/cmds/locksettings/Android.bp
@@ -21,8 +21,7 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-java_binary {
+sh_binary {
name: "locksettings",
- wrapper: "locksettings.sh",
- srcs: ["**/*.java"],
+ src: "locksettings.sh",
}
diff --git a/cmds/locksettings/locksettings.sh b/cmds/locksettings/locksettings.sh
index 0ef4fa9..2f8d868 100755
--- a/cmds/locksettings/locksettings.sh
+++ b/cmds/locksettings/locksettings.sh
@@ -1,6 +1,2 @@
#!/system/bin/sh
-# Script to start "locksettings" on the device
-#
-base=/system
-export CLASSPATH=$base/framework/locksettings.jar
-exec app_process $base/bin com.android.commands.locksettings.LockSettingsCmd "$@"
+cmd lock_settings "$@"
diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
deleted file mode 100644
index 7d9260a..0000000
--- a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 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.commands.locksettings;
-
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ShellCallback;
-
-import com.android.internal.os.BaseCommand;
-import com.android.internal.widget.ILockSettings;
-
-import java.io.FileDescriptor;
-import java.io.PrintStream;
-
-public final class LockSettingsCmd extends BaseCommand {
-
- public static void main(String[] args) {
- (new LockSettingsCmd()).run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- main(new String[] { "help" });
- }
-
- @Override
- public void onRun() throws Exception {
- ILockSettings lockSettings = ILockSettings.Stub.asInterface(
- ServiceManager.getService("lock_settings"));
- lockSettings.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out,
- FileDescriptor.err, getRawArgs(), new ShellCallback(), new ResultReceiver(null) {});
- }
-}
diff --git a/core/api/current.txt b/core/api/current.txt
index 2db3eae..206f42b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7269,6 +7269,13 @@
method public void onSharedElementsReady();
}
+ public final class StartForegroundCalledOnStoppedServiceException extends java.lang.IllegalStateException implements android.os.Parcelable {
+ ctor public StartForegroundCalledOnStoppedServiceException(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.StartForegroundCalledOnStoppedServiceException> CREATOR;
+ }
+
public class StatusBarManager {
method @RequiresPermission(android.Manifest.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE) public boolean canLaunchCaptureContentActivityForNote(@NonNull android.app.Activity);
method public void requestAddTileService(@NonNull android.content.ComponentName, @NonNull CharSequence, @NonNull android.graphics.drawable.Icon, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -32591,6 +32598,7 @@
field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 33; // 0x21
field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710
+ field public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -39854,6 +39862,7 @@
field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+ field public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6; // 0x6
field public static final int UI_TYPE_DIALOG = 3; // 0x3
field public static final int UI_TYPE_INLINE = 2; // 0x2
field public static final int UI_TYPE_MENU = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fbc30de..d73806b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5990,6 +5990,7 @@
method @Nullable public android.media.AudioFormat getCaptureFormat();
method public int getCaptureSession();
method public byte[] getData();
+ method public long getHalEventReceivedMillis();
method public boolean isCaptureAvailable();
}
@@ -9123,8 +9124,7 @@
method public int getProtocolCapability();
}
- public class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
- method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettings.Builder builder();
+ public final class IptvFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @IntRange(from=0) public long getBitrate();
method @NonNull public String getContentUrl();
method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
@@ -9145,6 +9145,7 @@
}
public static final class IptvFrontendSettings.Builder {
+ ctor public IptvFrontendSettings.Builder();
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setBitrate(@IntRange(from=0) long);
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setContentUrl(@NonNull String);
@@ -9157,8 +9158,7 @@
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettings.Builder setSrcPort(int);
}
- public class IptvFrontendSettingsFec {
- method @NonNull public static android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder builder();
+ public final class IptvFrontendSettingsFec {
method @IntRange(from=0) public int getFecColNum();
method @IntRange(from=0) public int getFecRowNum();
method public int getFecType();
@@ -9169,6 +9169,7 @@
}
public static final class IptvFrontendSettingsFec.Builder {
+ ctor public IptvFrontendSettingsFec.Builder();
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec build();
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecColNum(@IntRange(from=0) int);
method @NonNull public android.media.tv.tuner.frontend.IptvFrontendSettingsFec.Builder setFecRowNum(@IntRange(from=0) int);
@@ -10001,48 +10002,80 @@
package android.net.wifi.sharedconnectivity.app {
- public final class DeviceInfo implements android.os.Parcelable {
+ public final class HotspotNetwork implements android.os.Parcelable {
method public int describeContents();
- method @IntRange(from=0, to=100) public int getBatteryPercentage();
- method @IntRange(from=0, to=3) public int getConnectionStrength();
- method @NonNull public String getDeviceName();
- method public int getDeviceType();
- method @NonNull public String getModelName();
+ method public long getDeviceId();
+ method public int getHostNetworkType();
+ method @Nullable public String getHotspotBssid();
+ method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes();
+ method @Nullable public String getHotspotSsid();
+ method @NonNull public String getNetworkName();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo getNetworkProviderInfo();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.DeviceInfo> CREATOR;
- field public static final int DEVICE_TYPE_LAPTOP = 3; // 0x3
- field public static final int DEVICE_TYPE_PHONE = 1; // 0x1
- field public static final int DEVICE_TYPE_TABLET = 2; // 0x2
- field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.HotspotNetwork> CREATOR;
+ field public static final int NETWORK_TYPE_CELLULAR = 1; // 0x1
+ field public static final int NETWORK_TYPE_ETHERNET = 3; // 0x3
+ field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int NETWORK_TYPE_WIFI = 2; // 0x2
}
- public static final class DeviceInfo.Builder {
- ctor public DeviceInfo.Builder();
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo build();
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setBatteryPercentage(@IntRange(from=0, to=100) int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setConnectionStrength(@IntRange(from=0, to=3) int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setDeviceName(@NonNull String);
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setDeviceType(int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo.Builder setModelName(@NonNull String);
+ public static final class HotspotNetwork.Builder {
+ ctor public HotspotNetwork.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder addHotspotSecurityType(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setDeviceId(long);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHostNetworkType(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotBssid(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setHotspotSsid(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setNetworkName(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork.Builder setNetworkProviderInfo(@NonNull android.net.wifi.sharedconnectivity.app.NetworkProviderInfo);
+ }
+
+ public final class HotspotNetworkConnectionStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getExtras();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetwork getHotspotNetwork();
+ method public int getStatus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED = 9; // 0x9
+ field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT = 1; // 0x1
+ field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT_FAILED = 7; // 0x7
+ field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT = 8; // 0x8
+ field public static final int CONNECTION_STATUS_NO_CELL_DATA = 6; // 0x6
+ field public static final int CONNECTION_STATUS_PROVISIONING_FAILED = 3; // 0x3
+ field public static final int CONNECTION_STATUS_TETHERING_TIMEOUT = 4; // 0x4
+ field public static final int CONNECTION_STATUS_TETHERING_UNSUPPORTED = 5; // 0x5
+ field public static final int CONNECTION_STATUS_UNKNOWN = 0; // 0x0
+ field public static final int CONNECTION_STATUS_UNKNOWN_ERROR = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus> CREATOR;
+ }
+
+ public static final class HotspotNetworkConnectionStatus.Builder {
+ ctor public HotspotNetworkConnectionStatus.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.Builder setHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.Builder setStatus(int);
}
public final class KnownNetwork implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
+ method @Nullable public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo getNetworkProviderInfo();
method public int getNetworkSource();
method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes();
method @NonNull public String getSsid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.KnownNetwork> CREATOR;
- field public static final int NETWORK_SOURCE_CLOUD_SELF = 1; // 0x1
- field public static final int NETWORK_SOURCE_NEARBY_SELF = 0; // 0x0
+ field public static final int NETWORK_SOURCE_CLOUD_SELF = 2; // 0x2
+ field public static final int NETWORK_SOURCE_NEARBY_SELF = 1; // 0x1
+ field public static final int NETWORK_SOURCE_UNKNOWN = 0; // 0x0
}
public static final class KnownNetwork.Builder {
ctor public KnownNetwork.Builder();
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build();
- method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkProviderInfo(@Nullable android.net.wifi.sharedconnectivity.app.NetworkProviderInfo);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int);
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String);
}
@@ -10067,29 +10100,56 @@
method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.Builder setStatus(int);
}
+ public final class NetworkProviderInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=100) public int getBatteryPercentage();
+ method @IntRange(from=0, to=3) public int getConnectionStrength();
+ method @NonNull public String getDeviceName();
+ method public int getDeviceType();
+ method @NonNull public String getModelName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.NetworkProviderInfo> CREATOR;
+ field public static final int DEVICE_TYPE_AUTO = 5; // 0x5
+ field public static final int DEVICE_TYPE_LAPTOP = 3; // 0x3
+ field public static final int DEVICE_TYPE_PHONE = 1; // 0x1
+ field public static final int DEVICE_TYPE_TABLET = 2; // 0x2
+ field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int DEVICE_TYPE_WATCH = 4; // 0x4
+ }
+
+ public static final class NetworkProviderInfo.Builder {
+ ctor public NetworkProviderInfo.Builder();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo build();
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setBatteryPercentage(@IntRange(from=0, to=100) int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setConnectionStrength(@IntRange(from=0, to=3) int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceName(@NonNull String);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setDeviceType(int);
+ method @NonNull public android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.Builder setModelName(@NonNull String);
+ }
+
public interface SharedConnectivityClientCallback {
+ method public void onHotspotNetworkConnectionStatusChanged(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus);
+ method public void onHotspotNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.HotspotNetwork>);
method public void onKnownNetworkConnectionStatusChanged(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus);
method public void onKnownNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
method public void onRegisterCallbackFailed(@NonNull Exception);
method public void onServiceConnected();
method public void onServiceDisconnected();
method public void onSharedConnectivitySettingsChanged(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
- method public void onTetherNetworkConnectionStatusChanged(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus);
- method public void onTetherNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork>);
}
public class SharedConnectivityManager {
- method public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
- method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
- method public boolean disconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
- method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
- method @Nullable public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus getKnownNetworkConnectionStatus();
- method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork> getKnownNetworks();
- method @Nullable public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState getSettingsState();
- method @Nullable public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus getTetherNetworkConnectionStatus();
- method @NonNull public java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork> getTetherNetworks();
- method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
- method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean connectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean disconnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus getHotspotNetworkConnectionStatus();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.sharedconnectivity.app.HotspotNetwork> getHotspotNetworks();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus getKnownNetworkConnectionStatus();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork> getKnownNetworks();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState getSettingsState();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
}
public final class SharedConnectivitySettingsState implements android.os.Parcelable {
@@ -10107,62 +10167,6 @@
method @NonNull public android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState.Builder setInstantTetherEnabled(boolean);
}
- public final class TetherNetwork implements android.os.Parcelable {
- method public int describeContents();
- method public long getDeviceId();
- method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo();
- method @Nullable public String getHotspotBssid();
- method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes();
- method @Nullable public String getHotspotSsid();
- method @NonNull public String getNetworkName();
- method public int getNetworkType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.TetherNetwork> CREATOR;
- field public static final int NETWORK_TYPE_CELLULAR = 1; // 0x1
- field public static final int NETWORK_TYPE_ETHERNET = 3; // 0x3
- field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
- field public static final int NETWORK_TYPE_WIFI = 2; // 0x2
- }
-
- public static final class TetherNetwork.Builder {
- ctor public TetherNetwork.Builder();
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder addHotspotSecurityType(int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork build();
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceId(long);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotBssid(@NonNull String);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSsid(@NonNull String);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkName(@NonNull String);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkType(int);
- }
-
- public final class TetherNetworkConnectionStatus implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.os.Bundle getExtras();
- method public int getStatus();
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork getTetherNetwork();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED = 9; // 0x9
- field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT = 1; // 0x1
- field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT_FAILED = 7; // 0x7
- field public static final int CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT = 8; // 0x8
- field public static final int CONNECTION_STATUS_NO_CELL_DATA = 6; // 0x6
- field public static final int CONNECTION_STATUS_PROVISIONING_FAILED = 3; // 0x3
- field public static final int CONNECTION_STATUS_TETHERING_TIMEOUT = 4; // 0x4
- field public static final int CONNECTION_STATUS_TETHERING_UNSUPPORTED = 5; // 0x5
- field public static final int CONNECTION_STATUS_UNKNOWN = 0; // 0x0
- field public static final int CONNECTION_STATUS_UNKNOWN_ERROR = 2; // 0x2
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus> CREATOR;
- }
-
- public static final class TetherNetworkConnectionStatus.Builder {
- ctor public TetherNetworkConnectionStatus.Builder();
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus build();
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.Builder setExtras(@NonNull android.os.Bundle);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.Builder setStatus(int);
- method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.Builder setTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
- }
-
}
package android.net.wifi.sharedconnectivity.service {
@@ -10170,15 +10174,15 @@
public abstract class SharedConnectivityService extends android.app.Service {
ctor public SharedConnectivityService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onConnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
method public abstract void onConnectKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
- method public abstract void onConnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
- method public abstract void onDisconnectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
+ method public abstract void onDisconnectHotspotNetwork(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetwork);
method public abstract void onForgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
+ method public final void setHotspotNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.HotspotNetwork>);
method public final void setKnownNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
method public final void setSettingsState(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
- method public final void setTetherNetworks(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork>);
+ method public final void updateHotspotNetworkConnectionStatus(@NonNull android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus);
method public final void updateKnownNetworkConnectionStatus(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus);
- method public final void updateTetherNetworkConnectionStatus(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus);
}
}
@@ -13053,6 +13057,7 @@
method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
method @Nullable public byte[] getData();
method public int getDataFormat();
+ method public long getHalEventReceivedMillis();
method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras();
method @Deprecated @Nullable public byte[] getTriggerAudio();
@@ -13262,22 +13267,18 @@
public abstract class VisualQueryDetectionService extends android.app.Service implements android.service.voice.SandboxedDetectionServiceBase {
ctor public VisualQueryDetectionService();
+ method public final void finishQuery() throws java.lang.IllegalStateException;
+ method public final void gainedAttention();
+ method public final void lostAttention();
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onStartDetection(@NonNull android.service.voice.VisualQueryDetectionService.Callback);
+ method public void onStartDetection();
method public void onStopDetection();
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
+ method public final void rejectQuery() throws java.lang.IllegalStateException;
+ method public final void streamQuery(@NonNull String) throws java.lang.IllegalStateException;
field public static final String SERVICE_INTERFACE = "android.service.voice.VisualQueryDetectionService";
}
- public static final class VisualQueryDetectionService.Callback {
- ctor public VisualQueryDetectionService.Callback();
- method public void onAttentionGained();
- method public void onAttentionLost();
- method public void onQueryDetected(@NonNull String) throws java.lang.IllegalStateException;
- method public void onQueryFinished() throws java.lang.IllegalStateException;
- method public void onQueryRejected() throws java.lang.IllegalStateException;
- }
-
public final class VisualQueryDetectionServiceFailure extends android.service.voice.DetectorFailure {
method public int getErrorCode();
method public int getSuggestedAction();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 43fa6e2..6e6ec44 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1578,7 +1578,7 @@
}
public static class SoundTrigger.RecognitionEvent {
- ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
+ ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], long);
}
}
@@ -2796,7 +2796,7 @@
public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
method public void overrideAvailability(int);
method public void resetAvailability();
- method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, long, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
}
public static final class AlwaysOnHotwordDetector.EventPayload.Builder {
@@ -2808,6 +2808,7 @@
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureSession(int);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setData(@NonNull byte[]);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setDataFormat(int);
+ method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHalEventReceivedMillis(long);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHotwordDetectedResult(@NonNull android.service.voice.HotwordDetectedResult);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index a81ef18..322d23b 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -72,6 +72,13 @@
private static long sBackgroundPauseDelay = 10000;
/**
+ * A cache of the values in a list. Used so that when calling the list, we have a copy
+ * of it in case the list is modified while iterating. The array can be reused to avoid
+ * allocation on every notification.
+ */
+ private Object[] mCachedList;
+
+ /**
* Sets the duration for delaying pausing animators when apps go into the background.
* Used by AnimationHandler when requested to pause animators.
*
@@ -160,14 +167,7 @@
public void pause() {
if (isStarted() && !mPaused) {
mPaused = true;
- if (mPauseListeners != null) {
- ArrayList<AnimatorPauseListener> tmpListeners =
- (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationPause(this);
- }
- }
+ notifyPauseListeners(AnimatorCaller.ON_PAUSE);
}
}
@@ -184,14 +184,7 @@
public void resume() {
if (mPaused) {
mPaused = false;
- if (mPauseListeners != null) {
- ArrayList<AnimatorPauseListener> tmpListeners =
- (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationResume(this);
- }
- }
+ notifyPauseListeners(AnimatorCaller.ON_RESUME);
}
}
@@ -450,6 +443,7 @@
if (mPauseListeners != null) {
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
+ anim.mCachedList = null;
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
@@ -591,6 +585,70 @@
}
/**
+ * Calls notification for each AnimatorListener.
+ *
+ * @param notification The notification method to call on each listener.
+ * @param isReverse When this is used with start/end, this is the isReverse parameter. For
+ * other calls, this is ignored.
+ */
+ void notifyListeners(
+ AnimatorCaller<AnimatorListener, Animator> notification,
+ boolean isReverse
+ ) {
+ callOnList(mListeners, notification, this, isReverse);
+ }
+
+ /**
+ * Call pause/resume on each AnimatorPauseListener.
+ *
+ * @param notification Either ON_PAUSE or ON_RESUME to call onPause or onResume on each
+ * listener.
+ */
+ void notifyPauseListeners(AnimatorCaller<AnimatorPauseListener, Animator> notification) {
+ callOnList(mPauseListeners, notification, this, false);
+ }
+
+ /**
+ * Calls <code>call</code> for every item in <code>list</code> with <code>animator</code> and
+ * <code>isReverse</code> as parameters.
+ *
+ * @param list The list of items to make calls on.
+ * @param call The method to call for each item in list.
+ * @param animator The animator parameter of call.
+ * @param isReverse The isReverse parameter of call.
+ * @param <T> The item type of list
+ * @param <A> The Animator type of animator.
+ */
+ <T, A> void callOnList(
+ ArrayList<T> list,
+ AnimatorCaller<T, A> call,
+ A animator,
+ boolean isReverse
+ ) {
+ int size = list == null ? 0 : list.size();
+ if (size > 0) {
+ // Try to reuse mCacheList to store the items of list.
+ Object[] array;
+ if (mCachedList == null || mCachedList.length < size) {
+ array = new Object[size];
+ } else {
+ array = mCachedList;
+ // Clear it in case there is some reentrancy
+ mCachedList = null;
+ }
+ list.toArray(array);
+ for (int i = 0; i < size; i++) {
+ //noinspection unchecked
+ T item = (T) array[i];
+ call.call(item, animator, isReverse);
+ array[i] = null;
+ }
+ // Store it for the next call so we can reuse this array, if needed.
+ mCachedList = array;
+ }
+ }
+
+ /**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
@@ -748,4 +806,29 @@
return clone;
}
}
+
+ /**
+ * Internally used by {@link #callOnList(ArrayList, AnimatorCaller, Object, boolean)} to
+ * make a call on all children of a list. This can be for start, stop, pause, cancel, update,
+ * etc notifications.
+ *
+ * @param <T> The type of listener to make the call on
+ * @param <A> The type of animator that is passed as a parameter
+ */
+ interface AnimatorCaller<T, A> {
+ void call(T listener, A animator, boolean isReverse);
+
+ AnimatorCaller<AnimatorListener, Animator> ON_START = AnimatorListener::onAnimationStart;
+ AnimatorCaller<AnimatorListener, Animator> ON_END = AnimatorListener::onAnimationEnd;
+ AnimatorCaller<AnimatorListener, Animator> ON_CANCEL =
+ (listener, animator, isReverse) -> listener.onAnimationCancel(animator);
+ AnimatorCaller<AnimatorListener, Animator> ON_REPEAT =
+ (listener, animator, isReverse) -> listener.onAnimationRepeat(animator);
+ AnimatorCaller<AnimatorPauseListener, Animator> ON_PAUSE =
+ (listener, animator, isReverse) -> listener.onAnimationPause(animator);
+ AnimatorCaller<AnimatorPauseListener, Animator> ON_RESUME =
+ (listener, animator, isReverse) -> listener.onAnimationResume(animator);
+ AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator> ON_UPDATE =
+ (listener, animator, isReverse) -> listener.onAnimationUpdate(animator);
+ }
}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 257adfe..09eec9d 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -32,6 +32,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import java.util.function.Consumer;
/**
* This class plays a set of {@link Animator} objects in the specified order. Animations
@@ -424,24 +425,28 @@
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
if (isStarted()) {
- ArrayList<AnimatorListener> tmpListeners = null;
- if (mListeners != null) {
- tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
- int size = tmpListeners.size();
- for (int i = 0; i < size; i++) {
- tmpListeners.get(i).onAnimationCancel(this);
- }
- }
- ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
- int setSize = playingSet.size();
- for (int i = 0; i < setSize; i++) {
- playingSet.get(i).mAnimation.cancel();
- }
+ notifyListeners(AnimatorCaller.ON_CANCEL, false);
+ callOnPlayingSet(Animator::cancel);
mPlayingSet.clear();
endAnimation();
}
}
+ /**
+ * Calls consumer on every Animator of mPlayingSet.
+ *
+ * @param consumer The method to call on every Animator of mPlayingSet.
+ */
+ private void callOnPlayingSet(Consumer<Animator> consumer) {
+ final ArrayList<Node> list = mPlayingSet;
+ final int size = list.size();
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0; i < size; i++) {
+ final Animator animator = list.get(i).mAnimation;
+ consumer.accept(animator);
+ }
+ }
+
// Force all the animations to end when the duration scale is 0.
private void forceToEnd() {
if (mEndCanBeCalled) {
@@ -662,6 +667,7 @@
super.pause();
if (!previouslyPaused && mPaused) {
mPauseTime = -1;
+ callOnPlayingSet(Animator::pause);
}
}
@@ -676,6 +682,7 @@
if (mPauseTime >= 0) {
addAnimationCallback(0);
}
+ callOnPlayingSet(Animator::resume);
}
}
@@ -751,26 +758,14 @@
private void notifyStartListeners(boolean inReverse) {
if (mListeners != null && !mStartListenersCalled) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- AnimatorListener listener = tmpListeners.get(i);
- listener.onAnimationStart(this, inReverse);
- }
+ notifyListeners(AnimatorCaller.ON_START, inReverse);
}
mStartListenersCalled = true;
}
private void notifyEndListeners(boolean inReverse) {
if (mListeners != null && mStartListenersCalled) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- AnimatorListener listener = tmpListeners.get(i);
- listener.onAnimationEnd(this, inReverse);
- }
+ notifyListeners(AnimatorCaller.ON_END, inReverse);
}
mStartListenersCalled = false;
}
@@ -1503,6 +1498,7 @@
anim.mNodeMap = new ArrayMap<Animator, Node>();
anim.mNodes = new ArrayList<Node>(nodeCount);
anim.mEvents = new ArrayList<AnimationEvent>();
+ anim.mStartListenersCalled = false;
anim.mAnimationEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7009725..719f596 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1110,24 +1110,14 @@
private void notifyStartListeners(boolean isReversing) {
if (mListeners != null && !mStartListenersCalled) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationStart(this, isReversing);
- }
+ notifyListeners(AnimatorCaller.ON_START, isReversing);
}
mStartListenersCalled = true;
}
private void notifyEndListeners(boolean isReversing) {
if (mListeners != null && mStartListenersCalled) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(this, isReversing);
- }
+ notifyListeners(AnimatorCaller.ON_END, isReversing);
}
mStartListenersCalled = false;
}
@@ -1224,15 +1214,7 @@
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners(mReversing);
}
- int listenersSize = mListeners.size();
- if (listenersSize > 0) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- for (int i = 0; i < listenersSize; i++) {
- AnimatorListener listener = tmpListeners.get(i);
- listener.onAnimationCancel(this);
- }
- }
+ notifyListeners(AnimatorCaller.ON_CANCEL, false);
}
endAnimation();
@@ -1435,12 +1417,7 @@
done = true;
} else if (newIteration && !lastIterationFinished) {
// Time to repeat
- if (mListeners != null) {
- int numListeners = mListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- mListeners.get(i).onAnimationRepeat(this);
- }
- }
+ notifyListeners(AnimatorCaller.ON_REPEAT, false);
} else if (lastIterationFinished) {
done = true;
}
@@ -1493,13 +1470,8 @@
iteration = Math.min(iteration, mRepeatCount);
lastIteration = Math.min(lastIteration, mRepeatCount);
- if (iteration != lastIteration) {
- if (mListeners != null) {
- int numListeners = mListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- mListeners.get(i).onAnimationRepeat(this);
- }
- }
+ if (notify && iteration != lastIteration) {
+ notifyListeners(AnimatorCaller.ON_REPEAT, false);
}
}
@@ -1697,11 +1669,8 @@
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
- if (mUpdateListeners != null) {
- int numListeners = mUpdateListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- mUpdateListeners.get(i).onAnimationUpdate(this);
- }
+ if (mSeekFraction >= 0 || mStartListenersCalled) {
+ callOnList(mUpdateListeners, AnimatorCaller.ON_UPDATE, this, false);
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index b9f38d3..d810f05 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -968,14 +968,6 @@
public abstract void stopForegroundServiceDelegate(@NonNull ServiceConnection connection);
/**
- * Called by PowerManager. Return whether a given procstate is allowed to hold
- * wake locks in deep doze. Because it's called with the power manager lock held, we can't
- * hold AM locks in it.
- * @hide
- */
- public abstract boolean canHoldWakeLocksInDeepDoze(int uid, int procstate);
-
- /**
* Same as {@link android.app.IActivityManager#startProfile(int userId)}, but it would succeed
* even if the profile is disabled - it should only be called by
* {@link com.android.server.devicepolicy.DevicePolicyManagerService} when starting a profile
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 50275ab..dfdfd0e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -558,9 +558,6 @@
boolean hideForNow;
Configuration createdConfig;
Configuration overrideConfig;
- // TODO(b/263402465): pass deviceId directly in LaunchActivityItem#execute
- // The deviceId assigned by the server when this activity was first started.
- int mDeviceId;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
// Callback used for updating activity override config and camera compat control state.
@@ -623,7 +620,7 @@
}
public ActivityClientRecord(IBinder token, Intent intent, int ident,
- ActivityInfo info, Configuration overrideConfig, int deviceId,
+ ActivityInfo info, Configuration overrideConfig,
String referrer, IVoiceInteractor voiceInteractor, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
@@ -645,7 +642,6 @@
this.isForward = isForward;
this.profilerInfo = profilerInfo;
this.overrideConfig = overrideConfig;
- this.mDeviceId = deviceId;
this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo);
mActivityOptions = activityOptions;
mLaunchedFromBubble = launchedFromBubble;
@@ -3876,7 +3872,7 @@
*/
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
- PendingTransactionActions pendingActions, Intent customIntent) {
+ PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -3889,7 +3885,7 @@
// Make sure we are running with the most recent config.
mConfigurationController.handleConfigurationChanged(null, null);
- updateDeviceIdForNonUIContexts(r.mDeviceId);
+ updateDeviceIdForNonUIContexts(deviceId);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
@@ -5946,7 +5942,7 @@
r.startsNotResumed = startsNotResumed;
r.overrideConfig = overrideConfig;
- handleLaunchActivity(r, pendingActions, customIntent);
+ handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent);
}
@Override
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 6bb38e7..49fb794 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -188,7 +188,7 @@
/** Perform activity launch. */
public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
- PendingTransactionActions pendingActions, Intent customIntent);
+ PendingTransactionActions pendingActions, int deviceId, Intent customIntent);
/** Perform activity start. */
public abstract void handleStartActivity(@NonNull ActivityClientRecord r,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e3ec493..4713a31 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2814,7 +2814,7 @@
public @NonNull Context createDeviceContext(int deviceId) {
if (deviceId != Context.DEVICE_ID_DEFAULT) {
VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
- if (!vdm.isValidVirtualDeviceId(deviceId)) {
+ if (vdm == null || !vdm.isValidVirtualDeviceId(deviceId)) {
throw new IllegalArgumentException(
"Not a valid ID of the default device or any virtual device: " + deviceId);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index bf69531..dc197bd 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1401,95 +1401,99 @@
if (mApplication != null) {
return mApplication;
}
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
- synchronized (sApplications) {
- final Application cached = sApplications.get(mPackageName);
- if (cached != null) {
- // Looks like this is always happening for the system server, because
- // the LoadedApk created in systemMain() -> attach() isn't cached properly?
- if (!"android".equals(mPackageName)) {
- Slog.wtfStack(TAG, "App instance already created for package=" + mPackageName
- + " instance=" + cached);
- }
- if (!allowDuplicateInstances) {
- mApplication = cached;
- return cached;
- }
- // Some apps intentionally call makeApplication() to create a new Application
- // instance... Sigh...
- }
- }
- Application app = null;
-
- final String myProcessName = Process.myProcessName();
- String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
- myProcessName);
- if (forceDefaultAppClass || (appClass == null)) {
- appClass = "android.app.Application";
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
}
try {
- final java.lang.ClassLoader cl = getClassLoader();
- if (!mPackageName.equals("android")) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "initializeJavaContextClassLoader");
- initializeJavaContextClassLoader();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
-
- // Rewrite the R 'constants' for all library apks.
- SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
- false, false);
- for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
- final int id = packageIdentifiers.keyAt(i);
- if (id == 0x01 || id == 0x7f) {
- continue;
- }
-
- rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
- }
-
- ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
- // The network security config needs to be aware of multiple
- // applications in the same process to handle discrepancies
- NetworkSecurityConfigProvider.handleNewApplication(appContext);
- app = mActivityThread.mInstrumentation.newApplication(
- cl, appClass, appContext);
- appContext.setOuterContext(app);
- } catch (Exception e) {
- if (!mActivityThread.mInstrumentation.onException(app, e)) {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- throw new RuntimeException(
- "Unable to instantiate application " + appClass
- + " package " + mPackageName + ": " + e.toString(), e);
- }
- }
- mActivityThread.mAllApplications.add(app);
- mApplication = app;
- if (!allowDuplicateInstances) {
synchronized (sApplications) {
- sApplications.put(mPackageName, app);
- }
- }
-
- if (instrumentation != null) {
- try {
- instrumentation.callApplicationOnCreate(app);
- } catch (Exception e) {
- if (!instrumentation.onException(app, e)) {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- throw new RuntimeException(
- "Unable to create application " + app.getClass().getName()
- + ": " + e.toString(), e);
+ final Application cached = sApplications.get(mPackageName);
+ if (cached != null) {
+ // Looks like this is always happening for the system server, because
+ // the LoadedApk created in systemMain() -> attach() isn't cached properly?
+ if (!"android".equals(mPackageName)) {
+ Slog.wtfStack(TAG, "App instance already created for package="
+ + mPackageName + " instance=" + cached);
+ }
+ if (!allowDuplicateInstances) {
+ mApplication = cached;
+ return cached;
+ }
+ // Some apps intentionally call makeApplication() to create a new Application
+ // instance... Sigh...
}
}
+
+ Application app = null;
+
+ final String myProcessName = Process.myProcessName();
+ String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
+ myProcessName);
+ if (forceDefaultAppClass || (appClass == null)) {
+ appClass = "android.app.Application";
+ }
+
+ try {
+ final java.lang.ClassLoader cl = getClassLoader();
+ if (!mPackageName.equals("android")) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "initializeJavaContextClassLoader");
+ initializeJavaContextClassLoader();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ // Rewrite the R 'constants' for all library apks.
+ SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+ false, false);
+ for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
+ final int id = packageIdentifiers.keyAt(i);
+ if (id == 0x01 || id == 0x7f) {
+ continue;
+ }
+
+ rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
+ }
+
+ ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
+ // The network security config needs to be aware of multiple
+ // applications in the same process to handle discrepancies
+ NetworkSecurityConfigProvider.handleNewApplication(appContext);
+ app = mActivityThread.mInstrumentation.newApplication(
+ cl, appClass, appContext);
+ appContext.setOuterContext(app);
+ } catch (Exception e) {
+ if (!mActivityThread.mInstrumentation.onException(app, e)) {
+ throw new RuntimeException(
+ "Unable to instantiate application " + appClass
+ + " package " + mPackageName + ": " + e.toString(), e);
+ }
+ }
+ mActivityThread.mAllApplications.add(app);
+ mApplication = app;
+ if (!allowDuplicateInstances) {
+ synchronized (sApplications) {
+ sApplications.put(mPackageName, app);
+ }
+ }
+
+ if (instrumentation != null) {
+ try {
+ instrumentation.callApplicationOnCreate(app);
+ } catch (Exception e) {
+ if (!instrumentation.onException(app, e)) {
+ throw new RuntimeException(
+ "Unable to create application " + app.getClass().getName()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+
+ return app;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
-
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- return app;
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java b/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java
new file mode 100644
index 0000000..55885a6
--- /dev/null
+++ b/core/java/android/app/StartForegroundCalledOnStoppedServiceException.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Exception thrown when {@link Service#startForeground} is called on a service that's not
+ * actually started.
+ */
+public final class StartForegroundCalledOnStoppedServiceException
+ extends IllegalStateException implements Parcelable {
+ /**
+ * Constructor.
+ */
+ public StartForegroundCalledOnStoppedServiceException(@NonNull String message) {
+ super(message);
+ }
+
+ StartForegroundCalledOnStoppedServiceException(@NonNull Parcel source) {
+ super(source.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ }
+
+ public static final @NonNull Creator<StartForegroundCalledOnStoppedServiceException>
+ CREATOR = new Creator<StartForegroundCalledOnStoppedServiceException>() {
+ @NonNull
+ public StartForegroundCalledOnStoppedServiceException createFromParcel(
+ Parcel source) {
+ return new StartForegroundCalledOnStoppedServiceException(source);
+ }
+
+ @NonNull
+ public StartForegroundCalledOnStoppedServiceException[] newArray(int size) {
+ return new StartForegroundCalledOnStoppedServiceException[size];
+ }
+ };
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6d80a44..83e0153 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -877,6 +877,10 @@
@Override
public VirtualDeviceManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
+ if (!ctx.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
+ return null;
+ }
IVirtualDeviceManager service = IVirtualDeviceManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.VIRTUAL_DEVICE_SERVICE));
return new VirtualDeviceManager(service, ctx.getOuterContext());
@@ -1648,6 +1652,7 @@
case Context.ETHERNET_SERVICE:
case Context.CONTEXTHUB_SERVICE:
case Context.VIRTUALIZATION_SERVICE:
+ case Context.VIRTUAL_DEVICE_SERVICE:
return null;
}
Slog.wtf(TAG, "Manager wrapper not available: " + name);
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 3d0aa25..5833f1b 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -96,11 +96,11 @@
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
- mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor, mState, mPersistentState,
+ mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken);
- client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
+ client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index c8f7d10..bfab61f 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -32,6 +32,7 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.Context;
import android.os.IBinder;
import android.util.IntArray;
import android.util.Slog;
@@ -218,7 +219,7 @@
switch (state) {
case ON_CREATE:
mTransactionHandler.handleLaunchActivity(r, mPendingActions,
- null /* customIntent */);
+ Context.DEVICE_ID_INVALID, null /* customIntent */);
break;
case ON_START:
mTransactionHandler.handleStartActivity(r, mPendingActions,
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index d3502c52..aa302ad 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -20,7 +20,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myUserHandle;
-import static android.os.Trace.TRACE_TAG_DATABASE;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION;
import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_CHECK_URI_PERMISSION;
@@ -285,7 +285,7 @@
// Return an empty cursor for all columns.
return new MatrixCursor(cursor.getColumnNames(), 0);
}
- traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "query: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -296,7 +296,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -305,7 +305,7 @@
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getType: ", uri.getAuthority());
try {
if (checkGetTypePermission(attributionSource, uri)
== PermissionChecker.PERMISSION_GRANTED) {
@@ -330,7 +330,7 @@
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -417,7 +417,7 @@
setCallingAttributionSource(original);
}
}
- traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "insert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -426,7 +426,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -439,7 +439,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "bulkInsert: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -448,7 +448,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -485,7 +485,7 @@
}
}
}
- traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "applyBatch: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -504,7 +504,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -517,7 +517,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "delete: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -526,7 +526,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -539,7 +539,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return 0;
}
- traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "update: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -548,7 +548,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -559,7 +559,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -569,7 +569,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -580,7 +580,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, mode);
- traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -590,7 +590,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -599,7 +599,7 @@
String method, @Nullable String arg, @Nullable Bundle extras) {
validateIncomingAuthority(authority);
Bundle.setDefusable(extras, true);
- traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "call: ", authority);
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -608,7 +608,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -617,13 +617,13 @@
// getCallingPackage() isn't available in getType(), as the javadoc states.
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getStreamTypes: ", uri.getAuthority());
try {
return mInterface.getStreamTypes(uri, mimeTypeFilter);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -635,7 +635,7 @@
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
enforceFilePermission(attributionSource, uri, "r");
- traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openTypedAssetFile: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -645,7 +645,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -663,7 +663,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "canonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -672,7 +672,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -699,7 +699,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return null;
}
- traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "uncanonicalize: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -708,7 +708,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -735,7 +735,7 @@
!= PermissionChecker.PERMISSION_GRANTED) {
return false;
}
- traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "refresh: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -743,7 +743,7 @@
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -752,7 +752,7 @@
int uid, int modeFlags) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
- traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
+ traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "checkUriPermission: ", uri.getAuthority());
final AttributionSource original = setCallingAttributionSource(
attributionSource);
try {
@@ -761,7 +761,7 @@
throw e.rethrowAsRuntimeException();
} finally {
setCallingAttributionSource(original);
- Trace.traceEnd(TRACE_TAG_DATABASE);
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 658702f..d0accb7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5703,6 +5703,9 @@
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.companion.virtual.VirtualDeviceManager} for managing virtual devices.
*
+ * On devices without {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP}
+ * system feature the {@link #getSystemService(String)} will return {@code null}.
+ *
* @see #getSystemService(String)
* @see android.companion.virtual.VirtualDeviceManager
*/
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 9a2cd06..de18fa3 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -198,13 +198,13 @@
int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
// The API recognition event doesn't allow for a null audio format, even though it doesn't
// always make sense. We thus replace it with a default.
- AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(
- aidlEvent.audioConfig, true /*isInput*/);
- return new SoundTrigger.GenericRecognitionEvent(
- aidlEvent.status,
- modelHandle, aidlEvent.captureAvailable, captureSession,
- aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
- audioFormat, aidlEvent.data, aidlEvent.recognitionStillActive);
+ AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig,
+ true /*isInput*/);
+ // TODO(b/265852186) propagate a timestamp from aidl interfaces
+ return new SoundTrigger.GenericRecognitionEvent(aidlEvent.status, modelHandle,
+ aidlEvent.captureAvailable, captureSession, aidlEvent.captureDelayMs,
+ aidlEvent.capturePreambleMs, aidlEvent.triggerInData, audioFormat, aidlEvent.data,
+ aidlEvent.recognitionStillActive, -1 /* halEventReceivedMillis */);
}
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
@@ -217,14 +217,13 @@
}
// The API recognition event doesn't allow for a null audio format, even though it doesn't
// always make sense. We thus replace it with a default.
- AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(
- aidlEvent.common.audioConfig, true /*isInput*/);
+ AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig,
+ true /*isInput*/);
+ // TODO(b/265852186) propagate a timestamp from aidl interfaces
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
- aidlEvent.common.captureAvailable,
- captureSession, aidlEvent.common.captureDelayMs,
- aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
- audioFormat, aidlEvent.common.data,
- apiExtras);
+ aidlEvent.common.captureAvailable, captureSession, aidlEvent.common.captureDelayMs,
+ aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData, audioFormat,
+ aidlEvent.common.data, apiExtras, -1 /* halEventReceivedMillis */);
}
// In case of a null input returns a non-null valid output.
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b7a694c..01558a3 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -28,6 +28,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -36,17 +37,13 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioFormat;
-import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
-import android.media.permission.SafeCloseable;
import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -56,6 +53,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.os.SystemClock;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -1182,23 +1180,38 @@
* @hide
*/
public final boolean recognitionStillActive;
+ /**
+ * Timestamp of when the trigger event from SoundTriggerHal was received by the
+ * framework.
+ *
+ * <p>Clock monotonic including suspend time or its equivalent on the system,
+ * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
+ *
+ * <p>Value represents elapsed realtime in milliseconds when the event was received from the
+ * HAL. The value will be -1 if the event was not generated from the HAL.
+ *
+ * @hide
+ */
+ @ElapsedRealtimeLong
+ public final long halEventReceivedMillis;
/** @hide */
@TestApi
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs,
- boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) {
- this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
- capturePreambleMs, triggerInData, captureFormat, data,
- status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
+ boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
+ @ElapsedRealtimeLong long halEventReceivedMillis) {
+ this(status, soundModelHandle, captureAvailable,
+ captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
+ data, status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
}
/** @hide */
public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs,
boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
- boolean recognitionStillActive) {
+ boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
this.status = status;
this.soundModelHandle = soundModelHandle;
this.captureAvailable = captureAvailable;
@@ -1209,6 +1222,7 @@
this.captureFormat = requireNonNull(captureFormat);
this.data = data != null ? data : new byte[0];
this.recognitionStillActive = recognitionStillActive;
+ this.halEventReceivedMillis = halEventReceivedMillis;
}
/**
@@ -1251,6 +1265,21 @@
return data;
}
+ /**
+ * Timestamp of when the trigger event from SoundTriggerHal was received by the
+ * framework.
+ *
+ * Clock monotonic including suspend time or its equivalent on the system,
+ * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
+ *
+ * @return Elapsed realtime in milliseconds when the event was received from the HAL.
+ * Returns -1 if the event was not generated from the HAL.
+ */
+ @ElapsedRealtimeLong
+ public long getHalEventReceivedMillis() {
+ return halEventReceivedMillis;
+ }
+
/** @hide */
public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR
= new Parcelable.Creator<RecognitionEvent>() {
@@ -1285,9 +1314,10 @@
}
byte[] data = in.readBlob();
boolean recognitionStillActive = in.readBoolean();
+ long halEventReceivedMillis = in.readLong();
return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data,
- recognitionStillActive);
+ recognitionStillActive, halEventReceivedMillis);
}
/** @hide */
@@ -1314,6 +1344,7 @@
}
dest.writeBlob(data);
dest.writeBoolean(recognitionStillActive);
+ dest.writeLong(halEventReceivedMillis);
}
@Override
public int hashCode() {
@@ -1333,6 +1364,7 @@
result = prime * result + soundModelHandle;
result = prime * result + status;
result = result + (recognitionStillActive ? 1289 : 1291);
+ result = prime * result + Long.hashCode(halEventReceivedMillis);
return result;
}
@@ -1359,6 +1391,9 @@
return false;
if (soundModelHandle != other.soundModelHandle)
return false;
+ if (halEventReceivedMillis != other.halEventReceivedMillis) {
+ return false;
+ }
if (status != other.status)
return false;
if (triggerInData != other.triggerInData)
@@ -1395,6 +1430,7 @@
(", channelMask=" + captureFormat.getChannelMask()))
+ ", data=" + (data == null ? 0 : data.length)
+ ", recognitionStillActive=" + recognitionStillActive
+ + ", halEventReceivedMillis=" + halEventReceivedMillis
+ "]";
}
}
@@ -1776,19 +1812,22 @@
public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs,
boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
- @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) {
+ @Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
+ @ElapsedRealtimeLong long halEventReceivedMillis) {
this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
capturePreambleMs, triggerInData, captureFormat, data, keyphraseExtras,
- status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
+ status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
}
- public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ public KeyphraseRecognitionEvent(int status, int soundModelHandle,
+ boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs,
boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
@Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
- boolean recognitionStillActive) {
- super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
- capturePreambleMs, triggerInData, captureFormat, data, recognitionStillActive);
+ boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
+ super(status, soundModelHandle, captureAvailable,
+ captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
+ data, recognitionStillActive, halEventReceivedMillis);
this.keyphraseExtras =
keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
}
@@ -1825,11 +1864,13 @@
}
byte[] data = in.readBlob();
boolean recognitionStillActive = in.readBoolean();
+ long halEventReceivedMillis = in.readLong();
KeyphraseRecognitionExtra[] keyphraseExtras =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
- return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
- captureSession, captureDelayMs, capturePreambleMs, triggerInData,
- captureFormat, data, keyphraseExtras, recognitionStillActive);
+ return new KeyphraseRecognitionEvent(status, soundModelHandle,
+ captureAvailable, captureSession, captureDelayMs, capturePreambleMs,
+ triggerInData, captureFormat, data, keyphraseExtras, recognitionStillActive,
+ halEventReceivedMillis);
}
@Override
@@ -1851,6 +1892,7 @@
}
dest.writeBlob(data);
dest.writeBoolean(recognitionStillActive);
+ dest.writeLong(halEventReceivedMillis);
dest.writeTypedArray(keyphraseExtras, flags);
}
@@ -1885,9 +1927,11 @@
public String toString() {
return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
+ ", status=" + status
- + ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
- + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
- + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
+ + ", soundModelHandle=" + soundModelHandle
+ + ", captureAvailable=" + captureAvailable
+ + ", captureSession=" + captureSession
+ + ", captureDelayMs=" + captureDelayMs
+ + ", capturePreambleMs=" + capturePreambleMs
+ ", triggerInData=" + triggerInData
+ ((captureFormat == null) ? "" :
(", sampleRate=" + captureFormat.getSampleRate()))
@@ -1897,6 +1941,7 @@
(", channelMask=" + captureFormat.getChannelMask()))
+ ", data=" + (data == null ? 0 : data.length)
+ ", recognitionStillActive=" + recognitionStillActive
+ + ", halEventReceivedMillis=" + halEventReceivedMillis
+ "]";
}
}
@@ -1909,21 +1954,23 @@
*/
public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public GenericRecognitionEvent(int status, int soundModelHandle,
- boolean captureAvailable, int captureSession, int captureDelayMs,
- int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
- @Nullable byte[] data) {
- this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
+ public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ int captureSession, int captureDelayMs, int capturePreambleMs,
+ boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
+ @ElapsedRealtimeLong long halEventReceivedMillis) {
+ this(status, soundModelHandle, captureAvailable,
+ captureSession, captureDelayMs,
capturePreambleMs, triggerInData, captureFormat, data,
- status == RECOGNITION_STATUS_GET_STATE_RESPONSE);
+ status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis);
}
- public GenericRecognitionEvent(int status, int soundModelHandle,
- boolean captureAvailable, int captureSession, int captureDelayMs,
- int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
- @Nullable byte[] data, boolean recognitionStillActive) {
- super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
- capturePreambleMs, triggerInData, captureFormat, data, recognitionStillActive);
+ public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ int captureSession, int captureDelayMs, int capturePreambleMs,
+ boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
+ boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) {
+ super(status, soundModelHandle, captureAvailable,
+ captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
+ data, recognitionStillActive, halEventReceivedMillis);
}
public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR
@@ -1942,7 +1989,7 @@
return new GenericRecognitionEvent(event.status, event.soundModelHandle,
event.captureAvailable, event.captureSession, event.captureDelayMs,
event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data,
- event.recognitionStillActive);
+ event.recognitionStillActive, event.halEventReceivedMillis);
}
@Override
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9689be2..168db17 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1204,6 +1204,11 @@
* Upside Down Cake.
*/
public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT;
+
+ /**
+ * Vanilla Ice Cream.
+ */
+ public static final int VANILLA_ICE_CREAM = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8d8deaa..423fa42 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1818,6 +1818,13 @@
public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2;
/**
+ * A response code indicating that the specified user is removable.
+ *
+ * @hide
+ */
+ public static final int REMOVE_RESULT_USER_IS_REMOVABLE = 3;
+
+ /**
* A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that
* an unknown error occurred that prevented the user from being removed or set as ephemeral.
*
@@ -1872,6 +1879,7 @@
REMOVE_RESULT_REMOVED,
REMOVE_RESULT_DEFERRED,
REMOVE_RESULT_ALREADY_BEING_REMOVED,
+ REMOVE_RESULT_USER_IS_REMOVABLE,
REMOVE_RESULT_ERROR_USER_RESTRICTION,
REMOVE_RESULT_ERROR_USER_NOT_FOUND,
REMOVE_RESULT_ERROR_SYSTEM_USER,
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index b0e847c..5d58120e 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -233,6 +233,22 @@
*/
public static final int TYPE_DATASETS_SHOWN = 5;
+ /**
+ * The app/user requested for a field to be Autofilled.
+ *
+ * This event is fired when the view has been entered (by user or app) in order
+ * to differentiate from FillRequests that have been pretriggered for FillDialogs.
+ *
+ * For example, the user might navigate away from a screen without tapping any
+ * fields. In this case, a FillRequest/FillResponse has been generated, but was
+ * not used for Autofilling. The user did not intend to see an Autofill result,
+ * but a FillRequest was still generated. This is different from when the user
+ * did tap on a field after the pretriggered FillRequest, this event will appear
+ * in the FillEventHistory, signaling that the user did intend to Autofill
+ * something.
+ */
+ public static final int TYPE_VIEW_REQUESTED_AUTOFILL = 6;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_DATASET_SELECTED,
@@ -240,7 +256,8 @@
TYPE_AUTHENTICATION_SELECTED,
TYPE_SAVE_SHOWN,
TYPE_CONTEXT_COMMITTED,
- TYPE_DATASETS_SHOWN
+ TYPE_DATASETS_SHOWN,
+ TYPE_VIEW_REQUESTED_AUTOFILL
})
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
@@ -659,8 +676,8 @@
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications,
int saveDialogNotShowReason, int uiType) {
- mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,
- "eventType");
+ mEventType = Preconditions.checkArgumentInRange(eventType, 0,
+ TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
mDatasetId = datasetId;
mClientState = clientState;
mSelectedDatasetIds = selectedDatasetIds;
@@ -723,6 +740,8 @@
return "TYPE_CONTEXT_COMMITTED";
case TYPE_DATASETS_SHOWN:
return "TYPE_DATASETS_SHOWN";
+ case TYPE_VIEW_REQUESTED_AUTOFILL:
+ return "TYPE_VIEW_REQUESTED_AUTOFILL";
default:
return "TYPE_UNKNOWN";
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index b1dc686..3d741cf 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.RECORD_AUDIO;
import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,6 +55,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -401,6 +403,9 @@
private final ParcelFileDescriptor mAudioStream;
private final List<KeyphraseRecognitionExtra> mKephraseExtras;
+ @ElapsedRealtimeLong
+ private final long mHalEventReceivedMillis;
+
private EventPayload(boolean captureAvailable,
@Nullable AudioFormat audioFormat,
int captureSession,
@@ -408,7 +413,8 @@
@Nullable byte[] data,
@Nullable HotwordDetectedResult hotwordDetectedResult,
@Nullable ParcelFileDescriptor audioStream,
- @NonNull List<KeyphraseRecognitionExtra> keyphraseExtras) {
+ @NonNull List<KeyphraseRecognitionExtra> keyphraseExtras,
+ @ElapsedRealtimeLong long halEventReceivedMillis) {
mCaptureAvailable = captureAvailable;
mCaptureSession = captureSession;
mAudioFormat = audioFormat;
@@ -417,6 +423,7 @@
mHotwordDetectedResult = hotwordDetectedResult;
mAudioStream = audioStream;
mKephraseExtras = keyphraseExtras;
+ mHalEventReceivedMillis = halEventReceivedMillis;
}
/**
@@ -546,6 +553,21 @@
}
/**
+ * Timestamp of when the trigger event from SoundTriggerHal was received by the system
+ * server.
+ *
+ * Clock monotonic including suspend time or its equivalent on the system,
+ * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
+ *
+ * @return Elapsed realtime in milliseconds when the event was received from the HAL.
+ * Returns -1 if the event was not generated from the HAL.
+ */
+ @ElapsedRealtimeLong
+ public long getHalEventReceivedMillis() {
+ return mHalEventReceivedMillis;
+ }
+
+ /**
* Builder class for {@link EventPayload} objects
*
* @hide
@@ -561,6 +583,8 @@
private HotwordDetectedResult mHotwordDetectedResult = null;
private ParcelFileDescriptor mAudioStream = null;
private List<KeyphraseRecognitionExtra> mKeyphraseExtras = Collections.emptyList();
+ @ElapsedRealtimeLong
+ private long mHalEventReceivedMillis = -1;
public Builder() {}
@@ -579,6 +603,7 @@
setKeyphraseRecognitionExtras(
Arrays.asList(keyphraseRecognitionEvent.keyphraseExtras));
}
+ setHalEventReceivedMillis(keyphraseRecognitionEvent.getHalEventReceivedMillis());
}
/**
@@ -682,13 +707,27 @@
}
/**
+ * Timestamp of when the trigger event from SoundTriggerHal was received by the
+ * framework.
+ *
+ * Clock monotonic including suspend time or its equivalent on the system,
+ * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
+ */
+ @NonNull
+ public Builder setHalEventReceivedMillis(
+ @ElapsedRealtimeLong long halEventReceivedMillis) {
+ mHalEventReceivedMillis = halEventReceivedMillis;
+ return this;
+ }
+
+ /**
* Builds an {@link EventPayload} instance
*/
@NonNull
public EventPayload build() {
return new EventPayload(mCaptureAvailable, mAudioFormat, mCaptureSession,
mDataFormat, mData, mHotwordDetectedResult, mAudioStream,
- mKeyphraseExtras);
+ mKeyphraseExtras, mHalEventReceivedMillis);
}
}
}
@@ -905,8 +944,9 @@
@TestApi
@RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
public void triggerHardwareRecognitionEventForTest(int status, int soundModelHandle,
- boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs,
- boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
+ @ElapsedRealtimeLong long halEventReceivedMillis, boolean captureAvailable,
+ int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData,
+ @NonNull AudioFormat captureFormat, @Nullable byte[] data,
@NonNull List<KeyphraseRecognitionExtra> keyphraseRecognitionExtras) {
Log.d(TAG, "triggerHardwareRecognitionEventForTest()");
synchronized (mLock) {
@@ -919,7 +959,7 @@
new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
captureSession, captureDelayMs, capturePreambleMs, triggerInData,
captureFormat, data, keyphraseRecognitionExtras.toArray(
- new KeyphraseRecognitionExtra[0])),
+ new KeyphraseRecognitionExtra[0]), halEventReceivedMillis),
mInternalCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/service/voice/VisualQueryDetectionService.java b/core/java/android/service/voice/VisualQueryDetectionService.java
index 2e455c2..1783186 100644
--- a/core/java/android/service/voice/VisualQueryDetectionService.java
+++ b/core/java/android/service/voice/VisualQueryDetectionService.java
@@ -49,7 +49,7 @@
* {@link VoiceInteractionService#createVisualQueryDetector(PersistableBundle, SharedMemory,
* Executor, VisualQueryDetector.Callback)} is called, the system will bind the application's
* {@link VisualQueryDetectionService}. When requested from {@link VoiceInteractionService}, the
- * system calls into the {@link VisualQueryDetectionService#onStartDetection(Callback)} to enable
+ * system calls into the {@link VisualQueryDetectionService#onStartDetection()} to enable
* detection. This method MUST be implemented to support visual query detection service.
*
* Note: Methods in this class may be called concurrently.
@@ -78,6 +78,8 @@
/** @hide */
public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
+ private IDetectorSessionVisualQueryDetectionCallback mRemoteCallback = null;
+
private final ISandboxedDetectionService mInterface = new ISandboxedDetectionService.Stub() {
@@ -85,7 +87,8 @@
public void detectWithVisualSignals(
IDetectorSessionVisualQueryDetectionCallback callback) {
Log.v(TAG, "#detectWithVisualSignals");
- VisualQueryDetectionService.this.onStartDetection(new Callback(callback));
+ mRemoteCallback = callback;
+ VisualQueryDetectionService.this.onStartDetection();
}
@Override
@@ -178,16 +181,41 @@
/**
* This is called after the service is set up and the client should open the camera and the
- * microphone to start recognition.
- *
- * Called when the {@link VoiceInteractionService} requests that this service
- * {@link HotwordDetector#startRecognition()} start recognition on audio coming directly
+ * microphone to start recognition. When the {@link VoiceInteractionService} requests that this
+ * service {@link HotwordDetector#startRecognition()} start recognition on audio coming directly
* from the device microphone.
- *
- * @param callback The callback to use for responding to the detection request.
- *
+ * <p>
+ * Signal senders that return attention and query results are also expected to be called in this
+ * method according to the detection outcomes.
+ * <p>
+ * On successful user attention, developers should call
+ * {@link VisualQueryDetectionService#gainedAttention()} to enable the streaming of the query.
+ * <p>
+ * On user attention is lost, developers should call
+ * {@link VisualQueryDetectionService#lostAttention()} to disable the streaming of the query.
+ * <p>
+ * On query is detected and ready to stream, developers should call
+ * {@link VisualQueryDetectionService#streamQuery(String)} to return detected query to the
+ * {@link VisualQueryDetector}.
+ * <p>
+ * On streamed query should be rejected, clients should call
+ * {@link VisualQueryDetectionService#rejectQuery()} to abandon query streamed to the
+ * {@link VisualQueryDetector}.
+ * <p>
+ * On streamed query is finished, clients should call
+ * {@link VisualQueryDetectionService#finishQuery()} to complete query streamed to
+ * {@link VisualQueryDetector}.
+ * <p>
+ * Before a call for {@link VisualQueryDetectionService#streamQuery(String)} is triggered,
+ * {@link VisualQueryDetectionService#gainedAttention()} MUST be called to enable the streaming
+ * of query. A query streaming is also expected to be finished by calling either
+ * {@link VisualQueryDetectionService#finishQuery()} or
+ * {@link VisualQueryDetectionService#rejectQuery()} before a new query should start streaming.
+ * When the service enters the state where query streaming should be disabled,
+ * {@link VisualQueryDetectionService#lostAttention()} MUST be called to block unnecessary
+ * streaming.
*/
- public void onStartDetection(@NonNull Callback callback) {
+ public void onStartDetection() {
throw new UnsupportedOperationException();
}
@@ -199,118 +227,78 @@
}
/**
- * Callback for sending out signals and returning query results.
- *
- * On successful user attention, developers should call {@link Callback#onAttentionGained()}
- * to enable the streaming of the query.
- * <p>
- * On user attention is lost, developers should call {@link Callback#onAttentionLost()} to
- * disable the streaming of the query.
- * <p>
- * On query is detected and ready to stream, developers should call
- * {@link Callback#onQueryDetected(String)} to return detected query to the
- * {@link VisualQueryDetector}.
- * <p>
- * On streamed query should be rejected, clients should call {@link Callback#onQueryRejected()}
- * to abandon query streamed to the {@link VisualQueryDetector}.
- * <p>
- * On streamed query is finished, clients should call {@link Callback#onQueryFinished()} to
- * complete query streamed to {@link VisualQueryDetector}.
- * <p>
- * Before a call for {@link Callback#onQueryDetected(String)} is triggered,
- * {@link Callback#onAttentionGained()} MUST be called to enable the streaming of query. A query
- * streaming is also expected to be finished by calling either
- * {@link Callback#onQueryFinished()} or {@link Callback#onQueryRejected()} before a new query
- * should start streaming. When the service enters the state where query streaming should be
- * disabled, {@link Callback#onAttentionLost()} MUST be called to block unnecessary streaming.
+ * Informs the system that the user attention is gained so queries can be streamed.
*/
- public static final class Callback {
-
- // TODO: consider making the constructor a test api for testing purpose
- public Callback() {
- mRemoteCallback = null;
+ public final void gainedAttention() {
+ try {
+ mRemoteCallback.onAttentionGained();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- private final IDetectorSessionVisualQueryDetectionCallback mRemoteCallback;
-
- private Callback(IDetectorSessionVisualQueryDetectionCallback remoteCallback) {
- mRemoteCallback = remoteCallback;
+ /**
+ * Informs the system that the user attention is lost to stop streaming.
+ */
+ public final void lostAttention() {
+ try {
+ mRemoteCallback.onAttentionLost();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- /**
- * Informs attention listener that the user attention is gained.
- */
- public void onAttentionGained() {
- try {
- mRemoteCallback.onAttentionGained();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ /**
+ * Informs the {@link VisualQueryDetector} with the text content being captured about the
+ * query from the audio source. {@code partialQuery} is provided to the
+ * {@link VisualQueryDetector}. This method is expected to be only triggered if
+ * {@link VisualQueryDetectionService#gainedAttention()} is called to put the service into the
+ * attention gained state.
+ *
+ * @param partialQuery Partially detected query in string.
+ * @throws IllegalStateException if method called without attention gained.
+ */
+ public final void streamQuery(@NonNull String partialQuery) throws IllegalStateException {
+ Objects.requireNonNull(partialQuery);
+ try {
+ mRemoteCallback.onQueryDetected(partialQuery);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("#streamQuery must be only be triggered after "
+ + "calling #gainedAttention to be in the attention gained state.");
}
+ }
- /**
- * Informs attention listener that the user attention is lost.
- */
- public void onAttentionLost() {
- try {
- mRemoteCallback.onAttentionLost();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ /**
+ * Informs the {@link VisualQueryDetector} to abandon the streamed partial query that has
+ * been sent to {@link VisualQueryDetector}.This method is expected to be only triggered if
+ * {@link VisualQueryDetectionService#streamQuery(String)} is called to put the service into
+ * the query streaming state.
+ *
+ * @throws IllegalStateException if method called without query streamed.
+ */
+ public final void rejectQuery() throws IllegalStateException {
+ try {
+ mRemoteCallback.onQueryRejected();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("#rejectQuery must be only be triggered after "
+ + "calling #streamQuery to be in the query streaming state.");
}
+ }
- /**
- * Informs the {@link VisualQueryDetector} with the text content being captured about the
- * query from the audio source. {@code partialQuery} is provided to the
- * {@link VisualQueryDetector}. This method is expected to be only triggered if
- * {@link Callback#onAttentionGained()} is called to put the service into the attention
- * gained state.
- *
- * @param partialQuery Partially detected query in string.
- * @throws IllegalStateException if method called without attention gained.
- */
- public void onQueryDetected(@NonNull String partialQuery) throws IllegalStateException {
- Objects.requireNonNull(partialQuery);
- try {
- mRemoteCallback.onQueryDetected(partialQuery);
- } catch (RemoteException e) {
- throw new IllegalStateException("#onQueryDetected must be only be triggered after "
- + "calling #onAttentionGained to be in the attention gained state.");
- }
- }
-
- /**
- * Informs the {@link VisualQueryDetector} to abandon the streamed partial query that has
- * been sent to {@link VisualQueryDetector}.This method is expected to be only triggered if
- * {@link Callback#onQueryDetected()} is called to put the service into the query streaming
- * state.
- *
- * @throws IllegalStateException if method called without query streamed.
- */
- public void onQueryRejected() throws IllegalStateException {
- try {
- mRemoteCallback.onQueryRejected();
- } catch (RemoteException e) {
- throw new IllegalStateException("#onQueryRejected must be only be triggered after "
- + "calling #onQueryDetected to be in the query streaming state.");
- }
- }
-
- /**
- * Informs {@link VisualQueryDetector} with the metadata to complete the streamed partial
- * query that has been sent to {@link VisualQueryDetector}. This method is expected to be
- * only triggered if {@link Callback#onQueryDetected()} is called to put the service into
- * the query streaming state.
- *
- * @throws IllegalStateException if method called without query streamed.
- */
- public void onQueryFinished() throws IllegalStateException {
- try {
- mRemoteCallback.onQueryFinished();
- } catch (RemoteException e) {
- throw new IllegalStateException("#onQueryFinished must be only be triggered after "
- + "calling #onQueryDetected to be in the query streaming state.");
- }
+ /**
+ * Informs {@link VisualQueryDetector} with the metadata to complete the streamed partial
+ * query that has been sent to {@link VisualQueryDetector}. This method is expected to be
+ * only triggered if {@link VisualQueryDetectionService#streamQuery(String)} is called to put
+ * the service into the query streaming state.
+ *
+ * @throws IllegalStateException if method called without query streamed.
+ */
+ public final void finishQuery() throws IllegalStateException {
+ try {
+ mRemoteCallback.onQueryFinished();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("#finishQuery must be only be triggered after "
+ + "calling #streamQuery to be in the query streaming state.");
}
}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 0be3253..f0f6a4f 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -177,7 +177,8 @@
public interface Callback {
/**
- * Called when the {@link VisualQueryDetectionService} starts to stream partial queries.
+ * Called when the {@link VisualQueryDetectionService} starts to stream partial queries
+ * with {@link VisualQueryDetectionService#streamQuery(String)}.
*
* @param partialQuery The partial query in a text form being streamed.
*/
@@ -185,12 +186,13 @@
/**
* Called when the {@link VisualQueryDetectionService} decides to abandon the streamed
- * partial queries.
+ * partial queries with {@link VisualQueryDetectionService#rejectQuery()}.
*/
void onQueryRejected();
/**
- * Called when the {@link VisualQueryDetectionService} finishes streaming partial queries.
+ * Called when the {@link VisualQueryDetectionService} finishes streaming partial queries
+ * with {@link VisualQueryDetectionService#finishQuery()}.
*/
void onQueryFinished();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0b43eb5..b8bd703 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -144,8 +144,11 @@
/** The window should have no animation (by policy). */
public static final int FLAG_NO_ANIMATION = 1 << 18;
+ /** The task is launching behind home. */
+ public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 19;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 20;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -173,6 +176,7 @@
FLAG_IS_SYSTEM_WINDOW,
FLAG_BACK_GESTURE_ANIMATED,
FLAG_NO_ANIMATION,
+ FLAG_TASK_LAUNCHING_BEHIND,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -395,6 +399,9 @@
if ((flags & FLAG_NO_ANIMATION) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("NO_ANIMATION");
}
+ if ((flags & FLAG_TASK_LAUNCHING_BEHIND) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "TASK_LAUNCHING_BEHIND");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
index cd55d32..f2b0544 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -259,6 +259,7 @@
if (writeToFile) {
writeProtoLogToFileLocked();
logAndPrintln(pw, "Log written to " + mLogFile + ".");
+ mBuffer.resetBuffer();
}
if (mProtoLogEnabled) {
logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 2d5bb6c..c808d92 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -35,6 +35,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
@@ -205,6 +206,15 @@
*/
public static final int ACTION_REQUEST_IME_HIDDEN = 21;
+ /**
+ * Time it takes to load the animation frames in smart space doorbell card.
+ * It measures the duration from the images uris are passed into the view
+ * to all the frames are loaded.
+ * <p/>
+ * A long latency makes the doorbell animation looks janky until all the frames are loaded.
+ */
+ public static final int ACTION_SMARTSPACE_DOORBELL = 22;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -228,6 +238,7 @@
ACTION_SHOW_VOICE_INTERACTION,
ACTION_REQUEST_IME_SHOWN,
ACTION_REQUEST_IME_HIDDEN,
+ ACTION_SMARTSPACE_DOORBELL,
};
/** @hide */
@@ -254,6 +265,7 @@
ACTION_SHOW_VOICE_INTERACTION,
ACTION_REQUEST_IME_SHOWN,
ACTION_REQUEST_IME_HIDDEN,
+ ACTION_SMARTSPACE_DOORBELL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
@@ -283,6 +295,7 @@
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_SHOWN,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
};
private static LatencyTracker sLatencyTracker;
@@ -411,6 +424,8 @@
return "ACTION_REQUEST_IME_SHOWN";
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN:
return "ACTION_REQUEST_IME_HIDDEN";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL:
+ return "ACTION_SMARTSPACE_DOORBELL";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 59f6d2b..6fc6dc1 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -133,6 +133,21 @@
})
public @interface CredentialType {}
+ public static String credentialTypeToString(int credentialType) {
+ switch (credentialType) {
+ case CREDENTIAL_TYPE_NONE:
+ return "NONE";
+ case CREDENTIAL_TYPE_PATTERN:
+ return "PATTERN";
+ case CREDENTIAL_TYPE_PIN:
+ return "PIN";
+ case CREDENTIAL_TYPE_PASSWORD:
+ return "PASSWORD";
+ default:
+ return "UNKNOWN_" + credentialType;
+ }
+ }
+
/**
* Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
* method will return a handle to the Gatekeeper Password in the
@@ -1060,13 +1075,6 @@
}
/**
- * Set whether the visible password is enabled for cryptkeeper screen.
- */
- public void setVisiblePasswordEnabled(boolean enabled, int userId) {
- // No longer does anything.
- }
-
- /**
* Set and store the lockout deadline, meaning the user can't attempt their unlock
* pattern until the deadline has passed.
* @return the chosen deadline.
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index e029af4..f87d910 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -285,22 +285,24 @@
repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
}
+// Enum used in DNDModeProto to specify the zen mode setting.
+enum LoggedZenMode {
+ ROOT_CONFIG = -1; // Used to distinguish config (one per user) from the rules.
+ OFF = 0;
+ IMPORTANT_INTERRUPTIONS = 1;
+ NO_INTERRUPTIONS = 2;
+ ALARMS = 3;
+}
+
/**
* Atom that represents an item in the list of Do Not Disturb rules, pulled from
* NotificationManagerService.java.
*/
message DNDModeProto {
- enum Mode {
- ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules.
- ZEN_MODE_OFF = 0;
- ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
- ZEN_MODE_NO_INTERRUPTIONS = 2;
- ZEN_MODE_ALARMS = 3;
- }
optional int32 user = 1; // Android user ID (0, 1, 10, ...)
optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled
optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
- optional Mode zen_mode = 4;
+ optional LoggedZenMode zen_mode = 4;
// id is one of the system default rule IDs, or empty
// May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
optional string id = 5;
@@ -308,15 +310,34 @@
optional DNDPolicyProto policy = 7;
}
+// Enum used in DNDPolicyProto for a particular policy parameter's state.
+enum State {
+ STATE_UNSET = 0;
+ STATE_ALLOW = 1;
+ STATE_DISALLOW = 2;
+}
+
+// Enum used in DNDPolicyProto for which people are allowed to break through.
+enum PeopleType {
+ PEOPLE_UNSET = 0;
+ PEOPLE_ANYONE = 1;
+ PEOPLE_CONTACTS = 2;
+ PEOPLE_STARRED = 3;
+ PEOPLE_NONE = 4;
+}
+
+// Enum used in DNDPolicyProto for conversation types allowed to break through.
+enum ConversationType {
+ CONV_UNSET = 0;
+ CONV_ANYONE = 1;
+ CONV_IMPORTANT = 2;
+ CONV_NONE = 3;
+}
+
/**
- * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ * Message that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
*/
message DNDPolicyProto {
- enum State {
- STATE_UNSET = 0;
- STATE_ALLOW = 1;
- STATE_DISALLOW = 2;
- }
optional State calls = 1;
optional State repeat_callers = 2;
optional State messages = 3;
@@ -334,23 +355,8 @@
optional State ambient = 15;
optional State notification_list = 16;
- enum PeopleType {
- PEOPLE_UNSET = 0;
- PEOPLE_ANYONE = 1;
- PEOPLE_CONTACTS = 2;
- PEOPLE_STARRED = 3;
- PEOPLE_NONE = 4;
- }
-
optional PeopleType allow_calls_from = 17;
optional PeopleType allow_messages_from = 18;
- enum ConversationType {
- CONV_UNSET = 0;
- CONV_ANYONE = 1;
- CONV_IMPORTANT = 2;
- CONV_NONE = 3;
- }
-
optional ConversationType allow_conversations_from = 19;
}
diff --git a/core/res/res/color-watch/btn_watch_default_dark.xml b/core/res/res/color-watch/btn_watch_default_dark.xml
index 68b0eb6..333b44b 100644
--- a/core/res/res/color-watch/btn_watch_default_dark.xml
+++ b/core/res/res/color-watch/btn_watch_default_dark.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="?attr/colorPrimaryDark"/>
- <item android:color="?attr/colorPrimaryDark"/>
+ android:color="?attr/colorSurface"/>
+ <item android:color="?attr/colorSurface"/>
</selector>
diff --git a/core/res/res/color-watch/switch_track_watch_default_dark.xml b/core/res/res/color-watch/switch_track_watch_default_dark.xml
index 15bbeda..5af2566 100644
--- a/core/res/res/color-watch/switch_track_watch_default_dark.xml
+++ b/core/res/res/color-watch/switch_track_watch_default_dark.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="?android:colorPrimaryDark" />
- <item android:color="?android:colorPrimaryDark" />
+ android:color="?android:colorSurface" />
+ <item android:color="?android:colorSurface" />
</selector>
diff --git a/core/res/res/values-watch/colors_device_defaults.xml b/core/res/res/values-watch/colors_device_defaults.xml
index 6ffd6e6..ee9481c 100644
--- a/core/res/res/values-watch/colors_device_defaults.xml
+++ b/core/res/res/values-watch/colors_device_defaults.xml
@@ -16,102 +16,51 @@
<!-- Colors specific to Theme.DeviceDefault on watches, as specified via themes_device_default.xml
Note: These colors specifically proide a darker, high-contrast UI that is suitable for
- wearables with respect to 'glanceability'. OEM customization is supported within this set. -->
+ wearables with respect to 'glanceability'. -->
<resources>
<!--
accent_device_default_dark
- > from values/colors_material/accent_material_dark
- > from values/colors_material/material_deep_teal_200
- = #ff80cbc4
- ! replaced with custom color #5E97F6
- ! OEMS can customize as per specification
+ > from values/system_accent1_100
+ ! replaced with color/system_accent1_400
-->
- <color name="accent_device_default_dark">#5E97F6</color>
+ <color name="accent_device_default_dark">@color/system_accent1_400</color>
<!--
foreground_device_default_dark
- introduced to avoid coupling to foreground_material_dark
- colorForeground typically falls through Theme.DeviceDefault to Theme.Material
! fixed as white for optimal glanceability/contrast
- ! OEMs should not customize
-->
<color name="foreground_device_default_dark">@color/white</color>
<!--
background_device_default_dark
- > from values/colors_material/background_material_dark
- > from values/colors_material/material_grey_850
- = #ff303030
+ > from values/system_neutral1_900
! replaced with custom color #000000
- ! OEMs can customized as per specification
- (derived from accent color, constrained by brightness)
-->
<color name="background_device_default_dark">#000000</color>
- <!--
- background_floating_device_default_dark
- > from values/colors_material/background_floating_material_dark
- > from values/colors_material/material_grey_800
- = #ff424242
- ! replaced with custom color #1D2E4D
- (derived from accent color, constrained by brightness)
- -->
- <color name="background_floating_device_default_dark">#1D2E4D</color>
+ <!-- Derived from accent color at 20% luminance -->
+ <color name="background_floating_device_default_dark">@color/system_accent1_800</color>
<!--
- primary_device_default_dark
- > from values/colors_material/primary_material_dark
- > from values/colors_material/material_grey_900
- = #ff212121
- ! replaced with custom color #808080
- ! OEMs can customize as per specification
- (derived from background color + foreground @ 50% opacity)
- -->
- <color name="primary_device_default_dark">#808080</color>
+ primary_device_default_dark
+ > from values/colors/system_neutral1_900
+ ! replaced with system_neutral1_500
+ -->
+ <color name="primary_device_default_dark">@color/system_neutral1_500</color>
- <!--
- primary_dark_device_default_dark
- > from values/colors_material/primary_dark_material_dark
- = @color/black
- ! replaced with custom color #333333
- ! OEMS can customize as per specification
- (derived from background color + foreground @ 20% opacity)
- -->
- <color name="primary_dark_device_default_dark">#333333</color>
+ <!-- Currently matches the "surface dark" definition for phones. -->
+ <color name="surface_dark">@color/system_neutral1_800</color>
<!--
button_normal_device_default_dark
- - uses ?attr/disabledAlpha and ?attr/colorPrimaryDark to draw state list
+ - uses ?attr/disabledAlpha and ?attr/colorSurface to draw state list
(used as colorButtonNormal attribute in theme)
- see color-watch/btn_watch_default_dark.xml
-->
<color name="button_normal_device_default_dark">@color/btn_watch_default_dark</color>
- <!--
- error_color_device_default_dark
- - introduced to avoid coupling to error_color_mtterial (also #F4511E)
- - colorError typically falls through Theme.DeviceDefault to Theme.Material
- ! OEMs can customize as per specification
- -->
- <color name="error_color_device_default_dark">#F4511E</color>
-
- <!-- no customization required/suggested below this point -->
-
- <!--
- background_cache_hint_selector_device_default
- - note that this is based off of colors/background_cache_hint_selector_device_default
- xml drawable
- - uses ?attr/colorBackground and transparency to draw
- - no color customization required here
- -->
-
- <!-- deprecated for Wear
- these overrides exist only for compatibility with existing
- WTS theme test heuristics, based on the previous modifications
- to the material theme, they should not be used for customization
- as they are not exposed via publicly accessible attributes -->
- <color name="accent_device_default_dark_60_percent_opacity">#995E97f6</color>
- <color name="accent_device_default_700">#5385DB</color>
- <color name="accent_device_default_light">#75A4F5</color>
- <color name="accent_device_default_50">#93B7F5</color>
+ <!-- Matches the Wear Compose error color. -->
+ <color name="error_color_device_default_dark">#FF746E</color>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9252b14..f92b344 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9142,10 +9142,10 @@
{@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
<attr name="supportsLocalInteraction" format="boolean" />
<!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
- @hide @SystemApi -->
+ Expect a component name to be provided. @hide @SystemApi -->
<attr name="hotwordDetectionService" format="string" />
<!-- The service that provides {@link android.service.voice.VisualQueryDetectionService}.
- @hide @SystemApi -->
+ Expect a component name to be provided. @hide @SystemApi -->
<attr name="visualQueryDetectionService" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bf253ab..733e0ea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2798,8 +2798,10 @@
<integer name="config_userTypePackageWhitelistMode">13</integer> <!-- 1+4+8 -->
<!-- Whether the main user is a permanent admin user. If the main user is a permanent admin user
- it can't be deleted or downgraded to non-admin status. -->
- <bool name="config_isMainUserPermanentAdmin">false</bool>
+ it can't be deleted or downgraded to non-admin status.
+ This is generally only relevant on headless system user mode devices; on other devices, the
+ main user is the system user which is always a permanent admin anyway. -->
+ <bool name="config_isMainUserPermanentAdmin">true</bool>
<!-- Whether switch to headless system user is allowed. If allowed,
headless system user can run in the foreground even though it is not a full user. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 24a7d85..973b3a7 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -110,6 +110,10 @@
<string name="config_satellite_service_package" translatable="false"></string>
<java-symbol type="string" name="config_satellite_service_package" />
+ <!-- Telephony pointing UI package name to be launched. -->
+ <string name="config_pointing_ui_package" translatable="false"></string>
+ <java-symbol type="string" name="config_pointing_ui_package" />
+
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
supported. The network will be torn down on the source transport, and will be
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
new file mode 100644
index 0000000..22da0aa
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2023 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.animation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.PollingCheck;
+import android.view.View;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+@MediumTest
+public class AnimatorSetCallsTest {
+ @Rule
+ public final ActivityScenarioRule<AnimatorSetActivity> mRule =
+ new ActivityScenarioRule<>(AnimatorSetActivity.class);
+
+ private AnimatorSetActivity mActivity;
+ private AnimatorSet mSet1;
+ private ObjectAnimator mAnimator;
+ private CountListener mListener1;
+ private CountListener mListener2;
+ private CountListener mListener3;
+
+ @Before
+ public void setUp() throws Exception {
+ mRule.getScenario().onActivity((activity) -> {
+ mActivity = activity;
+ View square = mActivity.findViewById(R.id.square1);
+
+ mSet1 = new AnimatorSet();
+ mListener1 = new CountListener();
+ mSet1.addListener(mListener1);
+ mSet1.addPauseListener(mListener1);
+
+ AnimatorSet set2 = new AnimatorSet();
+ mListener2 = new CountListener();
+ set2.addListener(mListener2);
+ set2.addPauseListener(mListener2);
+
+ mAnimator = ObjectAnimator.ofFloat(square, "translationX", 0f, 100f);
+ mListener3 = new CountListener();
+ mAnimator.addListener(mListener3);
+ mAnimator.addPauseListener(mListener3);
+ mAnimator.setDuration(1);
+
+ set2.play(mAnimator);
+ mSet1.play(set2);
+ });
+ }
+
+ @Test
+ public void startEndCalledOnChildren() {
+ mRule.getScenario().onActivity((a) -> mSet1.start());
+ waitForOnUiThread(() -> mListener1.endForward > 0);
+
+ // only startForward and endForward should have been called once
+ mListener1.assertValues(
+ 1, 0, 1, 0, 0, 0, 0, 0
+ );
+ mListener2.assertValues(
+ 1, 0, 1, 0, 0, 0, 0, 0
+ );
+ mListener3.assertValues(
+ 1, 0, 1, 0, 0, 0, 0, 0
+ );
+ }
+
+ @Test
+ public void cancelCalledOnChildren() {
+ mRule.getScenario().onActivity((a) -> {
+ mSet1.start();
+ mSet1.cancel();
+ });
+ waitForOnUiThread(() -> mListener1.endForward > 0);
+
+ // only startForward and endForward should have been called once
+ mListener1.assertValues(
+ 1, 0, 1, 0, 1, 0, 0, 0
+ );
+ mListener2.assertValues(
+ 1, 0, 1, 0, 1, 0, 0, 0
+ );
+ mListener3.assertValues(
+ 1, 0, 1, 0, 1, 0, 0, 0
+ );
+ }
+
+ @Test
+ public void startEndReversedCalledOnChildren() {
+ mRule.getScenario().onActivity((a) -> mSet1.reverse());
+ waitForOnUiThread(() -> mListener1.endReverse > 0);
+
+ // only startForward and endForward should have been called once
+ mListener1.assertValues(
+ 0, 1, 0, 1, 0, 0, 0, 0
+ );
+ mListener2.assertValues(
+ 0, 1, 0, 1, 0, 0, 0, 0
+ );
+ mListener3.assertValues(
+ 0, 1, 0, 1, 0, 0, 0, 0
+ );
+ }
+
+ @Test
+ public void pauseResumeCalledOnChildren() {
+ mRule.getScenario().onActivity((a) -> {
+ mSet1.start();
+ mSet1.pause();
+ });
+ waitForOnUiThread(() -> mListener1.pause > 0);
+
+ // only startForward and pause should have been called once
+ mListener1.assertValues(
+ 1, 0, 0, 0, 0, 0, 1, 0
+ );
+ mListener2.assertValues(
+ 1, 0, 0, 0, 0, 0, 1, 0
+ );
+ mListener3.assertValues(
+ 1, 0, 0, 0, 0, 0, 1, 0
+ );
+
+ mRule.getScenario().onActivity((a) -> mSet1.resume());
+ waitForOnUiThread(() -> mListener1.endForward > 0);
+
+ // resume and endForward should have been called once
+ mListener1.assertValues(
+ 1, 0, 1, 0, 0, 0, 1, 1
+ );
+ mListener2.assertValues(
+ 1, 0, 1, 0, 0, 0, 1, 1
+ );
+ mListener3.assertValues(
+ 1, 0, 1, 0, 0, 0, 1, 1
+ );
+ }
+
+ @Test
+ public void updateOnlyWhileChangingValues() {
+ ArrayList<Float> updateValues = new ArrayList<>();
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateValues.add((Float) animation.getAnimatedValue());
+ }
+ });
+
+ mSet1.setCurrentPlayTime(0);
+
+ assertEquals(1, updateValues.size());
+ assertEquals(0f, updateValues.get(0), 0f);
+ }
+ @Test
+ public void updateOnlyWhileRunning() {
+ ArrayList<Float> updateValues = new ArrayList<>();
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateValues.add((Float) animation.getAnimatedValue());
+ }
+ });
+
+ mRule.getScenario().onActivity((a) -> {
+ mSet1.start();
+ });
+
+ waitForOnUiThread(() -> mListener1.endForward > 0);
+
+ // the duration is only 1ms, so there should only be two values, 0 and 100.
+ assertEquals(0f, updateValues.get(0), 0f);
+ assertEquals(100f, updateValues.get(updateValues.size() - 1), 0f);
+
+ // now check all the values in the middle, which can never go from 100->0.
+ boolean isAtEnd = false;
+ for (int i = 1; i < updateValues.size() - 1; i++) {
+ float actual = updateValues.get(i);
+ if (actual == 100f) {
+ isAtEnd = true;
+ }
+ float expected = isAtEnd ? 100f : 0f;
+ assertEquals(expected, actual, 0f);
+ }
+ }
+
+ private void waitForOnUiThread(PollingCheck.PollingCheckCondition condition) {
+ final boolean[] value = new boolean[1];
+ PollingCheck.waitFor(() -> {
+ mActivity.runOnUiThread(() -> value[0] = condition.canProceed());
+ return value[0];
+ });
+ }
+
+ private static class CountListener implements Animator.AnimatorListener,
+ Animator.AnimatorPauseListener {
+ public int startNoParam;
+ public int endNoParam;
+ public int startReverse;
+ public int startForward;
+ public int endForward;
+ public int endReverse;
+ public int cancel;
+ public int repeat;
+ public int pause;
+ public int resume;
+
+ public void assertValues(
+ int startForward,
+ int startReverse,
+ int endForward,
+ int endReverse,
+ int cancel,
+ int repeat,
+ int pause,
+ int resume
+ ) {
+ assertEquals(0, startNoParam);
+ assertEquals(0, endNoParam);
+ assertEquals(startForward, this.startForward);
+ assertEquals(startReverse, this.startReverse);
+ assertEquals(endForward, this.endForward);
+ assertEquals(endReverse, this.endReverse);
+ assertEquals(cancel, this.cancel);
+ assertEquals(repeat, this.repeat);
+ assertEquals(pause, this.pause);
+ assertEquals(resume, this.resume);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation, boolean isReverse) {
+ if (isReverse) {
+ startReverse++;
+ } else {
+ startForward++;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation, boolean isReverse) {
+ if (isReverse) {
+ endReverse++;
+ } else {
+ endForward++;
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ startNoParam++;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endNoParam++;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancel++;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ repeat++;
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ pause++;
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ resume++;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 81cd4da..8cc88ea 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -135,11 +135,15 @@
* @throws Exception
*/
@Before
- public void setUp() throws Exception {
+ public void setUp() throws Throwable {
final BasicAnimatorActivity activity = mActivityRule.getActivity();
Button button = activity.findViewById(R.id.animatingButton);
mAnimator = button.animate().x(100).y(100);
+ mActivityRule.runOnUiThread(() -> {
+ mAnimator.start();
+ mAnimator.cancel();
+ });
// mListener is the main testing mechanism of this file. The asserts of each test
// are embedded in the listener callbacks that it implements.
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 96811be..29d7902 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -40,7 +40,6 @@
"platform-test-annotations",
"truth-prebuilt",
"testables",
- "ub-uiautomator",
],
libs: [
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 539eb62..be2c27d 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -51,6 +51,7 @@
import android.app.LoadedApk;
import android.app.servertransaction.PendingTransactionActions;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -293,7 +294,7 @@
private Activity launchActivity(ActivityClientRecord r) {
return mThread.handleLaunchActivity(r, null /* pendingActions */,
- null /* customIntent */);
+ Context.DEVICE_ID_DEFAULT, null /* customIntent */);
}
private void startActivity(ActivityClientRecord r) {
@@ -347,7 +348,7 @@
doNothing().when(packageInfo).updateApplicationInfo(any(), any());
return new ActivityClientRecord(mock(IBinder.class), Intent.makeMainActivity(component),
- 0 /* ident */, info, new Configuration(), 0 /*deviceId */, null /* referrer */,
+ 0 /* ident */, info, new Configuration(), null /* referrer */,
null /* voiceInteractor */, null /* state */, null /* persistentState */,
null /* pendingResults */, null /* pendingNewIntents */,
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
index f6e3f2e..c53ddd7 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_decor_handle_menu.xml
@@ -63,38 +63,46 @@
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="0.5" />
- <Button
+ <ImageButton
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/fullscreen_button"
android:contentDescription="@string/fullscreen_text"
- android:background="@drawable/caption_fullscreen_button"/>
+ android:src="@drawable/caption_fullscreen_button"
+ android:scaleType="fitCenter"
+ android:background="?android:selectableItemBackgroundBorderless"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
- <Button
+ <ImageButton
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/split_screen_button"
android:contentDescription="@string/split_screen_text"
- android:background="@drawable/caption_split_screen_button"/>
+ android:src="@drawable/caption_split_screen_button"
+ android:scaleType="fitCenter"
+ android:background="?android:selectableItemBackgroundBorderless"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
- <Button
+ <ImageButton
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/floating_button"
android:contentDescription="@string/float_button_text"
- android:background="@drawable/caption_floating_button"/>
+ android:src="@drawable/caption_floating_button"
+ android:scaleType="fitCenter"
+ android:background="?android:selectableItemBackgroundBorderless"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
- <Button
+ <ImageButton
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/desktop_button"
android:contentDescription="@string/desktop_text"
- android:background="@drawable/caption_desktop_button"/>
+ android:src="@drawable/caption_desktop_button"
+ android:scaleType="fitCenter"
+ android:background="?android:selectableItemBackgroundBorderless"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
index e8edad1..413cfd7 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
@@ -16,9 +16,7 @@
<com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/system_neutral1_900">
+ style="@style/LetterboxDialog">
<!-- The background of the top-level layout acts as the background dim. -->
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
index ba9852c..5aff415 100644
--- a/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/letterbox_restart_dialog_layout.xml
@@ -16,14 +16,10 @@
<com.android.wm.shell.compatui.RestartDialogLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/system_neutral1_900">
+ style="@style/LetterboxDialog">
<!-- The background of the top-level layout acts as the background dim. -->
- <!--TODO (b/266288912): Resolve overdraw warning -->
-
<!-- Vertical margin will be set dynamically since it depends on task bounds.
Setting the alpha of the dialog container to 0, since it shouldn't be visible until the
enter animation starts. -->
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 6e750a3..965ab15 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -41,6 +41,9 @@
<color name="letterbox_education_accent_primary">@android:color/system_accent1_100</color>
<color name="letterbox_education_text_secondary">@android:color/system_neutral2_200</color>
+ <!-- Letterbox Dialog -->
+ <color name="letterbox_dialog_background">@android:color/system_neutral1_900</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
<color name="GM2_grey_700">#5F6368</color>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 0a0c49f..bc2e71d 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -38,11 +38,9 @@
</style>
<style name="CaptionWindowingButtonStyle">
- <item name="android:layout_width">32dp</item>
- <item name="android:layout_height">32dp</item>
+ <item name="android:layout_width">40dp</item>
+ <item name="android:layout_height">40dp</item>
<item name="android:padding">4dp</item>
- <item name="android:layout_marginTop">5dp</item>
- <item name="android:layout_marginBottom">5dp</item>
</style>
<style name="CaptionMenuButtonStyle" parent="@style/Widget.AppCompat.Button.Borderless">
@@ -80,6 +78,12 @@
<item name="android:textColor">@color/tv_pip_edu_text</item>
</style>
+ <style name="LetterboxDialog" parent="@android:style/Theme.Holo">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:background">@color/letterbox_dialog_background</item>
+ </style>
+
<style name="RestartDialogTitleText">
<item name="android:textSize">24sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index fee9140..e6f47d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -32,6 +32,7 @@
import android.hardware.input.InputManager;
import android.os.Looper;
import android.provider.DeviceConfig;
+import android.util.Log;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.InputChannel;
@@ -243,6 +244,7 @@
@VisibleForTesting
void onInputEvent(InputEvent ev) {
+ Log.d(TAG, "onInputEvent: " + ev);
if (!mEnableDragCornerResize && !mEnablePinchResize) {
// No need to handle anything if neither form of resizing is enabled.
return;
@@ -256,6 +258,17 @@
if (ev instanceof MotionEvent) {
MotionEvent mv = (MotionEvent) ev;
int action = mv.getActionMasked();
+
+ // TODO: remove logging once b/269505548 is resolved
+ if (action == MotionEvent.ACTION_MOVE && mFirstIndex != -1 && mSecondIndex != -1) {
+ float x0 = mv.getRawX(mFirstIndex);
+ float y0 = mv.getRawY(mFirstIndex);
+ float x1 = mv.getRawX(mSecondIndex);
+ float y1 = mv.getRawY(mSecondIndex);
+ Log.d(TAG, "at onInputEvent (" + x0 + ", " + y0 + ")");
+ Log.d(TAG, "at onInputEvent (" + x1 + ", " + y1 + ")");
+ }
+
final Rect pipBounds = mPipBoundsState.getBounds();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (!pipBounds.contains((int) mv.getRawX(), (int) mv.getRawY())
@@ -393,6 +406,7 @@
@VisibleForTesting
void onPinchResize(MotionEvent ev) {
+ Log.d(TAG, "onPinchResize: " + ev);
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
@@ -436,6 +450,10 @@
mLastPoint.set(x0, y0);
mLastSecondPoint.set(x1, y1);
+ // TODO: remove logging once b/269505548 is resolved
+ Log.d(TAG, "at onPinchResize (" + x0 + ", " + y0 + ")");
+ Log.d(TAG, "at onPinchResize (" + x1 + ", " + y1 + ")");
+
// Capture inputs
if (!mThresholdCrossed
&& (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 427d79e..3ed5ec5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -205,7 +205,7 @@
// and exit, since exit itself can trigger a number of changes that update the stages.
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
- private boolean mIsSplitEntering;
+ private boolean mIsDividerRemoteAnimating;
private boolean mIsDropEntering;
private boolean mIsExiting;
@@ -882,7 +882,7 @@
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
- mIsSplitEntering = true;
+ mIsDividerRemoteAnimating = true;
if (mSplitRequest == null) {
mSplitRequest = new SplitRequest(mainTaskId,
mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
@@ -975,7 +975,7 @@
}
private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSplitRequest = null;
// If any stage has no child after animation finished, it means that split will display
@@ -1241,7 +1241,7 @@
}
});
mShouldUpdateRecents = false;
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
@@ -1584,7 +1584,7 @@
&& !ENABLE_SHELL_TRANSITIONS) {
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config.
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mSplitLayout.update(null /* t */);
onLayoutSizeChanged(mSplitLayout);
}
@@ -1634,9 +1634,9 @@
}
void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
- if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
- && !mIsSplitEntering) {
- // Handle entring split case here if split already running background.
+ // Handle entering split screen while there is a split pair running in the background.
+ if (stageListener == mSideStageListener && !isSplitScreenVisible()
+ && mSplitRequest == null) {
if (mIsDropEntering) {
mSplitLayout.resetDividerPosition();
} else {
@@ -1728,7 +1728,7 @@
mDividerVisible = visible;
sendSplitVisibilityChanged();
- if (mIsSplitEntering) {
+ if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
@@ -1748,7 +1748,7 @@
" Skip animating divider bar due to divider leash not ready.");
return;
}
- if (mIsSplitEntering) {
+ if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
@@ -1816,7 +1816,8 @@
mSplitLayout.flingDividerToDismiss(
mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
- } else if (!isSplitScreenVisible() && !mIsSplitEntering) {
+ } else if (!isSplitScreenVisible() && mSplitRequest == null) {
+ // Dismiss split screen in the background once any sides of the split become empty.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index fac0461..47a116b 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="com.android.wm.shell.tests">
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.VIBRATE"/>
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.mock" />
diff --git a/libs/androidfw/ApkParsing.cpp b/libs/androidfw/ApkParsing.cpp
index 32d2c5b..7eedfdb 100644
--- a/libs/androidfw/ApkParsing.cpp
+++ b/libs/androidfw/ApkParsing.cpp
@@ -56,6 +56,11 @@
return nullptr;
}
+ // Make sure file starts with 'lib/' prefix.
+ if (strncmp(fileName, APK_LIB.data(), APK_LIB_LEN) != 0) {
+ return nullptr;
+ }
+
// Make sure there aren't subdirectories by checking if the next / after lib/ is the last slash
if (memchr(fileName + APK_LIB_LEN, '/', fileNameLen - APK_LIB_LEN) != lastSlash) {
return nullptr;
diff --git a/libs/androidfw/tests/ApkParsing_test.cpp b/libs/androidfw/tests/ApkParsing_test.cpp
index 62e88c6..ac1dc9b 100644
--- a/libs/androidfw/tests/ApkParsing_test.cpp
+++ b/libs/androidfw/tests/ApkParsing_test.cpp
@@ -74,4 +74,10 @@
auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
ASSERT_THAT(lastSlash, IsNull());
}
+
+TEST(ApkParsingTest, InvalidPrefix) {
+ const char* path = "assets/libhello.so";
+ auto lastSlash = util::ValidLibraryPathLastSlash(path, false, false);
+ ASSERT_THAT(lastSlash, IsNull());
+}
}
\ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b0896da..33c9eac 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -21,7 +21,7 @@
#ifdef __ANDROID__
#include "HWUIProperties.sysprop.h"
#endif
-#include "SkTraceEventCommon.h"
+#include "src/core/SkTraceEventCommon.h"
#include <algorithm>
#include <cstdlib>
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index b58f517..c67b135 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -18,9 +18,8 @@
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
-#include "include/private/SkFixed.h"
-#include "src/core/SkTSearch.h"
+#include <cstdlib>
#include <log/log.h>
typedef int Dot14;
@@ -41,18 +40,18 @@
if (x <= 0) {
return 0;
}
- if (x >= SK_Scalar1) {
+ if (x >= 1.0f) {
return Dot14_ONE;
}
- return SkScalarToFixed(x) >> 2;
+ return static_cast<Dot14>(x * Dot14_ONE);
}
static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) {
// pin to the unit-square, and convert to 2.14
Dot14 x = pin_and_convert(value);
- if (x == 0) return 0;
- if (x == Dot14_ONE) return SK_Scalar1;
+ if (x == 0) return 0.0f;
+ if (x == Dot14_ONE) return 1.0f;
Dot14 b = pin_and_convert(bx);
Dot14 c = pin_and_convert(cx);
@@ -84,7 +83,7 @@
A = 3 * b;
B = 3 * (c - 2 * b);
C = 3 * (b - c) + Dot14_ONE;
- return SkFixedToScalar(eval_cubic(t, A, B, C) << 2);
+ return Dot14ToFloat(eval_cubic(t, A, B, C));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -104,7 +103,7 @@
fFlags = 0;
fElemCount = static_cast<uint8_t>(elemCount);
fFrameCount = static_cast<int16_t>(frameCount);
- fRepeat = SK_Scalar1;
+ fRepeat = 1.0f;
if (fStorage) {
free(fStorage);
fStorage = nullptr;
@@ -136,17 +135,46 @@
float SkiaInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime,
const float blend[4]) {
- SkASSERT(time > prevTime && time < nextTime);
+ LOG_FATAL_IF(time < prevTime || time > nextTime);
float t = (float)(time - prevTime) / (float)(nextTime - prevTime);
return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
}
+// Returns the index of where the item is or the bit not of the index
+// where the item should go in order to keep arr sorted in ascending order.
+int SkiaInterpolatorBase::binarySearch(const SkTimeCode* arr, int count, SkMSec target) {
+ if (count <= 0) {
+ return ~0;
+ }
+
+ int lo = 0;
+ int hi = count - 1;
+
+ while (lo < hi) {
+ int mid = (hi + lo) / 2;
+ SkMSec elem = arr[mid].fTime;
+ if (elem == target) {
+ return mid;
+ } else if (elem < target) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+ // Check to see if target is greater or less than where we stopped
+ if (target < arr[lo].fTime) {
+ return ~lo;
+ }
+ // e.g. it should go at the end.
+ return ~(lo + 1);
+}
+
SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T, int* indexPtr,
bool* exactPtr) const {
- SkASSERT(fFrameCount > 0);
+ LOG_FATAL_IF(fFrameCount <= 0);
Result result = kNormal_Result;
- if (fRepeat != SK_Scalar1) {
+ if (fRepeat != 1.0f) {
SkMSec startTime = 0, endTime = 0; // initialize to avoid warning
this->getDuration(&startTime, &endTime);
SkMSec totalTime = endTime - startTime;
@@ -168,10 +196,8 @@
time = offsetTime + startTime;
}
- int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, sizeof(SkTimeCode));
-
+ int index = SkiaInterpolatorBase::binarySearch(fTimes, fFrameCount, time);
bool exact = true;
-
if (index < 0) {
index = ~index;
if (index == 0) {
@@ -184,10 +210,11 @@
}
result = kFreezeEnd_Result;
} else {
+ // Need to interpolate between two frames.
exact = false;
}
}
- SkASSERT(index < fFrameCount);
+ LOG_FATAL_IF(index >= fFrameCount);
const SkTimeCode* nextTime = &fTimes[index];
SkMSec nextT = nextTime[0].fTime;
if (exact) {
@@ -207,7 +234,7 @@
}
SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) {
- SkASSERT(elemCount > 0);
+ LOG_FATAL_IF(elemCount <= 0);
this->reset(elemCount, frameCount);
}
@@ -221,21 +248,19 @@
fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount);
}
-#define SK_Fixed1Third (SK_Fixed1 / 3)
-#define SK_Fixed2Third (SK_Fixed1 * 2 / 3)
-
static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f};
bool SkiaInterpolator::setKeyFrame(int index, SkMSec time, const float values[],
const float blend[4]) {
- SkASSERT(values != nullptr);
+ LOG_FATAL_IF(values == nullptr);
if (blend == nullptr) {
blend = gIdentityBlend;
}
- bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode));
- SkASSERT(success);
+ // Verify the time should go after all the frames before index
+ bool success = ~index == SkiaInterpolatorBase::binarySearch(fTimes, index, time);
+ LOG_FATAL_IF(!success);
if (success) {
SkTimeCode* timeCode = &fTimes[index];
timeCode->fTime = time;
@@ -257,7 +282,7 @@
if (exact) {
memcpy(values, nextSrc, fElemCount * sizeof(float));
} else {
- SkASSERT(index > 0);
+ LOG_FATAL_IF(index <= 0);
const float* prevSrc = nextSrc - fElemCount;
diff --git a/libs/hwui/SkiaInterpolator.h b/libs/hwui/SkiaInterpolator.h
index 9422cb5..62e6c1e 100644
--- a/libs/hwui/SkiaInterpolator.h
+++ b/libs/hwui/SkiaInterpolator.h
@@ -68,14 +68,16 @@
enum Flags { kMirror = 1, kReset = 2, kHasBlend = 4 };
static float ComputeRelativeT(uint32_t time, uint32_t prevTime, uint32_t nextTime,
const float blend[4] = nullptr);
- int16_t fFrameCount;
- uint8_t fElemCount;
- uint8_t fFlags;
- float fRepeat;
struct SkTimeCode {
uint32_t fTime;
float fBlend[4];
};
+ static int binarySearch(const SkTimeCode* arr, int count, uint32_t target);
+
+ int16_t fFrameCount;
+ uint8_t fElemCount;
+ uint8_t fFlags;
+ float fRepeat;
SkTimeCode* fTimes; // pointer into fStorage
void* fStorage;
};
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 15e529e..a66d3b8 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -1,11 +1,11 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "SkData.h"
-#include "SkMalloc.h"
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
#include "Utils.h"
+#include <cstdlib>
#include <nativehelper/JNIHelp.h>
#include <log/log.h>
#include <memory>
@@ -177,6 +177,10 @@
return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions);
}
+static void free_pointer_skproc(const void* ptr, void*) {
+ free((void*)ptr);
+}
+
sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray storage) {
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, inputStream, storage));
if (!stream) {
@@ -186,19 +190,31 @@
size_t bufferSize = 4096;
size_t streamLen = 0;
size_t len;
- char* data = (char*)sk_malloc_throw(bufferSize);
+ char* data = (char*)malloc(bufferSize);
+ LOG_ALWAYS_FATAL_IF(!data);
while ((len = stream->read(data + streamLen,
bufferSize - streamLen)) != 0) {
streamLen += len;
if (streamLen == bufferSize) {
bufferSize *= 2;
- data = (char*)sk_realloc_throw(data, bufferSize);
+ data = (char*)realloc(data, bufferSize);
+ LOG_ALWAYS_FATAL_IF(!data);
}
}
- data = (char*)sk_realloc_throw(data, streamLen);
-
- return SkData::MakeFromMalloc(data, streamLen);
+ if (streamLen == 0) {
+ // realloc with size 0 is unspecified behavior in C++11
+ free(data);
+ data = nullptr;
+ } else {
+ // Trim down the buffer to the actual size of the data.
+ LOG_FATAL_IF(streamLen > bufferSize);
+ data = (char*)realloc(data, streamLen);
+ LOG_ALWAYS_FATAL_IF(!data);
+ }
+ // Just in case sk_free differs from free, we ask Skia to use
+ // free to cleanup the buffer that SkData wraps.
+ return SkData::MakeWithProc(data, streamLen, free_pointer_skproc, nullptr);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 048ce02..cbd4520 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -1,6 +1,5 @@
#include "GraphicsJNI.h"
#include "SkMaskFilter.h"
-#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
#include "SkBlurTypes.h"
#include "SkTableMaskFilter.h"
@@ -11,6 +10,13 @@
}
}
+// From https://skia.googlesource.com/skia/+/d74c99a3cd5eef5f16b2eb226e6b45fe523c8552/src/core/SkBlurMask.cpp#28
+static constexpr float kBLUR_SIGMA_SCALE = 0.57735f;
+
+static float convertRadiusToSigma(float radius) {
+ return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+}
+
class SkMaskFilterGlue {
public:
static void destructor(JNIEnv* env, jobject, jlong filterHandle) {
@@ -19,7 +25,7 @@
}
static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
- SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+ SkScalar sigma = convertRadiusToSigma(radius);
SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release();
ThrowIAE_IfNull(env, filter);
return reinterpret_cast<jlong>(filter);
@@ -34,7 +40,7 @@
direction[i] = values[i];
}
- SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+ SkScalar sigma = convertRadiusToSigma(radius);
SkMaskFilter* filter = SkBlurMaskFilter::MakeEmboss(sigma,
direction, ambient, specular).release();
ThrowIAE_IfNull(env, filter);
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
index ba75102..65247a1 100644
--- a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettings.java
@@ -34,7 +34,7 @@
* @hide
*/
@SystemApi
-public class IptvFrontendSettings extends FrontendSettings {
+public final class IptvFrontendSettings extends FrontendSettings {
/** @hide */
@IntDef(prefix = "PROTOCOL_",
value = {PROTOCOL_UNDEFINED, PROTOCOL_UDP, PROTOCOL_RTP})
@@ -181,14 +181,6 @@
}
/**
- * Creates a builder for {@link IptvFrontendSettings}.
- */
- @NonNull
- public static Builder builder() {
- return new Builder();
- }
-
- /**
* Builder for {@link IptvFrontendSettings}.
*/
public static final class Builder {
@@ -202,7 +194,7 @@
private long mBitrate = 0;
private String mContentUrl = "";
- private Builder() {
+ public Builder() {
}
/**
@@ -309,8 +301,8 @@
*/
@NonNull
public IptvFrontendSettings build() {
- return new IptvFrontendSettings(mSrcIpAddress, mDstIpAddress, mSrcPort,
- mDstPort, mFec, mProtocol, mIgmp, mBitrate, mContentUrl);
+ return new IptvFrontendSettings(mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort,
+ mFec, mProtocol, mIgmp, mBitrate, mContentUrl);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
index a70af17..12eebee 100644
--- a/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
+++ b/media/java/android/media/tv/tuner/frontend/IptvFrontendSettingsFec.java
@@ -31,7 +31,7 @@
* @hide
*/
@SystemApi
-public class IptvFrontendSettingsFec {
+public final class IptvFrontendSettingsFec {
/** @hide */
@IntDef(prefix = "FEC_TYPE_",
value = {FEC_TYPE_UNDEFINED, FEC_TYPE_COLUMN, FEC_TYPE_ROW, FEC_TYPE_COLUMN_ROW})
@@ -93,14 +93,6 @@
}
/**
- * Creates a builder for {@link IptvFrontendSettingsFec}.
- */
- @NonNull
- public static Builder builder() {
- return new Builder();
- }
-
- /**
* Builder for {@link IptvFrontendSettingsFec}.
*/
public static final class Builder {
@@ -108,7 +100,7 @@
private int mFecRowNum;
private int mFecColNum;
- private Builder() {
+ public Builder() {
}
/**
diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml
index 61b4ffa..103a3d2 100644
--- a/packages/CredentialManager/res/values-de/strings.xml
+++ b/packages/CredentialManager/res/values-de/strings.xml
@@ -47,7 +47,8 @@
<string name="other_password_manager" msgid="565790221427004141">"Andere Passwortmanager"</string>
<string name="close_sheet" msgid="1393792015338908262">"Tabellenblatt schließen"</string>
<string name="accessibility_back_arrow_button" msgid="3233198183497842492">"Zurück zur vorherigen Seite"</string>
- <string name="accessibility_close_button" msgid="1163435587545377687">"Schließen"</string>
+ <!-- no translation found for accessibility_close_button (1163435587545377687) -->
+ <skip />
<!-- no translation found for accessibility_snackbar_dismiss (3456598374801836120) -->
<skip />
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Gespeicherten Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> verwenden?"</string>
@@ -61,7 +62,8 @@
<string name="get_dialog_heading_locked_password_managers" msgid="8911514851762862180">"Gesperrte Passwortmanager"</string>
<string name="locked_credential_entry_label_subtext_tap_to_unlock" msgid="6390367581393605009">"Zum Entsperren tippen"</string>
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Keine Anmeldedaten"</string>
- <string name="no_sign_in_info_in" msgid="2641118151920288356">"Keine Anmeldedaten in <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <!-- no translation found for no_sign_in_info_in (2641118151920288356) -->
+ <skip />
<string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Anmeldedaten verwalten"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Von einem anderen Gerät"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Anderes Gerät verwenden"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 0eaaf97..bffa40e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -305,7 +305,7 @@
},
navigationIcon = {
IconButton(
- modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp),
+ modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp).size(48.dp),
onClick = onNavigationIconClicked
) {
Box(
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index b60aba8..e6710ff 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -83,7 +83,7 @@
}
UserManager customUserManager = UninstallUninstalling.this
- .createContextAsUser(UserHandle.of(user.getIdentifier()), 0)
+ .createContextAsUser(user, 0)
.getSystemService(UserManager.class);
if (customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)) {
isCloneUser = true;
@@ -117,7 +117,7 @@
int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
- getPackageManager().getPackageInstaller().uninstall(
+ createContextAsUser(user, 0).getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
flags, pendingIntent.getIntentSender());
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
old mode 100755
new mode 100644
index 7250bdd..9c67817
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -367,10 +367,10 @@
int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
- getPackageManager().getPackageInstaller().uninstall(
- new VersionedPackage(mDialogInfo.appInfo.packageName,
- PackageManager.VERSION_CODE_HIGHEST),
- flags, pendingIntent.getIntentSender());
+ createContextAsUser(mDialogInfo.user, 0).getPackageManager().getPackageInstaller()
+ .uninstall(new VersionedPackage(mDialogInfo.appInfo.packageName,
+ PackageManager.VERSION_CODE_HIGHEST), flags,
+ pendingIntent.getIntentSender());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
index 2887872..464328e 100644
--- a/packages/SettingsLib/Spa/OWNERS
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -4,3 +4,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml
index 24efe28..be88620 100644
--- a/packages/SettingsLib/res/values-hi/arrays.xml
+++ b/packages/SettingsLib/res/values-hi/arrays.xml
@@ -63,17 +63,13 @@
<item msgid="6336372935919715515">"चालू और फ़िल्टर किया गया"</item>
<item msgid="2779123106632690576">"चालू है"</item>
</string-array>
- <string-array name="bt_hci_snoop_log_filters_entries">
- <item msgid="195768089203590086">"सिर्फ़ ACL हेडर छोड़ दें"</item>
- <item msgid="2776218217644557831">"A2DP मीडिया पैकेट फ़िल्टर करें"</item>
- <item msgid="8163235976612675092">"RFCOMM चैनल फ़िल्टर करें"</item>
- </string-array>
- <string-array name="bt_hci_snoop_log_profile_filter_entries">
- <item msgid="3961868665260627524">"बंद करें"</item>
- <item msgid="2505973306504851132">"कैरेक्टर स्ट्रिंग डालें"</item>
- <item msgid="5883011000629613855">"सिर्फ़ हेडर छोड़ दें"</item>
- <item msgid="1051534112762023603">"पूरी तरह से हटाएं"</item>
- </string-array>
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) -->
<string-array name="bluetooth_avrcp_versions">
<item msgid="6603880723315236832">"AVRCP 1.5 (डिफ़ॉल्ट)"</item>
<item msgid="1637054408779685086">"AVRCP 1.3"</item>
diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml
index 3ee7131..efdd879 100644
--- a/packages/SettingsLib/res/values-ms/arrays.xml
+++ b/packages/SettingsLib/res/values-ms/arrays.xml
@@ -63,17 +63,13 @@
<item msgid="6336372935919715515">"Didayakan Ditapis"</item>
<item msgid="2779123106632690576">"Didayakan"</item>
</string-array>
- <string-array name="bt_hci_snoop_log_filters_entries">
- <item msgid="195768089203590086">"Tinggalkan pengepala ACL sahaja"</item>
- <item msgid="2776218217644557831">"Tapis paket media A2DP"</item>
- <item msgid="8163235976612675092">"Tapis saluran RFCOMM"</item>
- </string-array>
- <string-array name="bt_hci_snoop_log_profile_filter_entries">
- <item msgid="3961868665260627524">"Lumpuhkan"</item>
- <item msgid="2505973306504851132">"Isi dengan rentetan aksara"</item>
- <item msgid="5883011000629613855">"Tinggalkan pengepala sahaja"</item>
- <item msgid="1051534112762023603">"Alih keluar sepenuhnya"</item>
- </string-array>
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) -->
<string-array name="bluetooth_avrcp_versions">
<item msgid="6603880723315236832">"AVRCP 1.5 (Lalai)"</item>
<item msgid="1637054408779685086">"AVRCP 1.3"</item>
diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
index 5d23239..0553aac 100644
--- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml
@@ -63,17 +63,13 @@
<item msgid="6336372935919715515">"Filtrado ativado"</item>
<item msgid="2779123106632690576">"Ativado"</item>
</string-array>
- <string-array name="bt_hci_snoop_log_filters_entries">
- <item msgid="195768089203590086">"Deixe apenas cabeçalhos de LCA (Lista de controlo de acesso)"</item>
- <item msgid="2776218217644557831">"Filtre pacotes de multimédia A2DP"</item>
- <item msgid="8163235976612675092">"Filtre o canal RFCOMM"</item>
- </string-array>
- <string-array name="bt_hci_snoop_log_profile_filter_entries">
- <item msgid="3961868665260627524">"Desativar"</item>
- <item msgid="2505973306504851132">"Preencha com uma string de carateres"</item>
- <item msgid="5883011000629613855">"Deixe apenas o cabeçalho"</item>
- <item msgid="1051534112762023603">"Remova totalmente"</item>
- </string-array>
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) -->
<string-array name="bluetooth_avrcp_versions">
<item msgid="6603880723315236832">"AVRCP 1.5 (predefinição)"</item>
<item msgid="1637054408779685086">"AVRCP 1.3"</item>
diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml
index 4b08f47..3e53ae6 100644
--- a/packages/SettingsLib/res/values-uz/arrays.xml
+++ b/packages/SettingsLib/res/values-uz/arrays.xml
@@ -63,17 +63,13 @@
<item msgid="6336372935919715515">"Filtrlar yoniq"</item>
<item msgid="2779123106632690576">"Yoniq"</item>
</string-array>
- <string-array name="bt_hci_snoop_log_filters_entries">
- <item msgid="195768089203590086">"Faqat ACL sarlavhalari qolsin"</item>
- <item msgid="2776218217644557831">"A2DP media paketlarni filtrlash"</item>
- <item msgid="8163235976612675092">"RFCOMM kanalini filtrlash"</item>
- </string-array>
- <string-array name="bt_hci_snoop_log_profile_filter_entries">
- <item msgid="3961868665260627524">"Faolsizlantirish"</item>
- <item msgid="2505973306504851132">"Qatorni harflar bilan toʻldiring"</item>
- <item msgid="5883011000629613855">"Faqat sarlavha qolsin"</item>
- <item msgid="1051534112762023603">"Butunlay olib tashlash"</item>
- </string-array>
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:0 (195768089203590086) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:1 (2776218217644557831) -->
+ <!-- no translation found for bt_hci_snoop_log_filters_entries:2 (8163235976612675092) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:0 (3961868665260627524) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:1 (2505973306504851132) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:2 (5883011000629613855) -->
+ <!-- no translation found for bt_hci_snoop_log_profile_filter_entries:3 (1051534112762023603) -->
<string-array name="bluetooth_avrcp_versions">
<item msgid="6603880723315236832">"AVRCP 1.5 (asosiy)"</item>
<item msgid="1637054408779685086">"AVRCP 1.3"</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 684a9aa..c9e8312 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -31,7 +31,6 @@
import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
-import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
@@ -621,9 +620,11 @@
dispatchConnectedDeviceChanged(id);
}
+ /**
+ * Ignore callback here since we'll also receive {@link onRequestFailed} with reason code.
+ */
@Override
public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {
- dispatchOnRequestFailed(REASON_UNKNOWN_ERROR);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 6b9866b..071ab27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -33,7 +33,6 @@
import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION;
import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION_MANAGED;
import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER;
import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM;
import static android.media.RouteListingPreference.Item.SUBTEXT_DEVICE_LOW_POWER;
@@ -45,6 +44,7 @@
import static android.media.RouteListingPreference.Item.SUBTEXT_UNAUTHORIZED;
import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -95,6 +95,17 @@
int TYPE_CAST_GROUP_DEVICE = 7;
}
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SelectionBehavior.SELECTION_BEHAVIOR_NONE,
+ SELECTION_BEHAVIOR_TRANSFER,
+ SelectionBehavior.SELECTION_BEHAVIOR_GO_TO_APP
+ })
+ public @interface SelectionBehavior {
+ int SELECTION_BEHAVIOR_NONE = 0;
+ int SELECTION_BEHAVIOR_TRANSFER = 1;
+ int SELECTION_BEHAVIOR_GO_TO_APP = 2;
+ }
+
@VisibleForTesting
int mType;
@@ -213,7 +224,7 @@
*
* @return selection behavior of device
*/
- @RouteListingPreference.Item.SubText
+ @SelectionBehavior
public int getSelectionBehavior() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && mItem != null
? mItem.getSelectionBehavior() : SELECTION_BEHAVIOR_TRANSFER;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f63c06a..270fda8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -30,6 +30,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -799,12 +800,12 @@
}
@Test
- public void onTransferFailed_shouldDispatchOnRequestFailed() {
+ public void onTransferFailed_notDispatchOnRequestFailed() {
mInfoMediaManager.registerCallback(mCallback);
mInfoMediaManager.mMediaRouterCallback.onTransferFailed(null, null);
- verify(mCallback).onRequestFailed(REASON_UNKNOWN_ERROR);
+ verify(mCallback, never()).onRequestFailed(REASON_UNKNOWN_ERROR);
}
@Test
diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/AnimatorExtensions.kt b/packages/SystemUI/animation/src/com/android/systemui/util/AnimatorExtensions.kt
new file mode 100644
index 0000000..35dbb89
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/util/AnimatorExtensions.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import androidx.core.animation.Animator
+
+/**
+ * Add an action which will be invoked when the animation has ended.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.end
+ */
+inline fun Animator.doOnEnd(
+ crossinline action: (animator: Animator) -> Unit
+): Animator.AnimatorListener = addListener(onEnd = action)
+
+/**
+ * Add an action which will be invoked when the animation has started.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.start
+ */
+inline fun Animator.doOnStart(
+ crossinline action: (animator: Animator) -> Unit
+): Animator.AnimatorListener = addListener(onStart = action)
+
+/**
+ * Add an action which will be invoked when the animation has been cancelled.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ * @see Animator.cancel
+ */
+inline fun Animator.doOnCancel(
+ crossinline action: (animator: Animator) -> Unit
+): Animator.AnimatorListener = addListener(onCancel = action)
+
+/**
+ * Add an action which will be invoked when the animation has repeated.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ */
+inline fun Animator.doOnRepeat(
+ crossinline action: (animator: Animator) -> Unit
+): Animator.AnimatorListener = addListener(onRepeat = action)
+
+/**
+ * Add a listener to this Animator using the provided actions.
+ *
+ * @return the [Animator.AnimatorListener] added to the Animator
+ */
+inline fun Animator.addListener(
+ crossinline onEnd: (animator: Animator) -> Unit = {},
+ crossinline onStart: (animator: Animator) -> Unit = {},
+ crossinline onCancel: (animator: Animator) -> Unit = {},
+ crossinline onRepeat: (animator: Animator) -> Unit = {}
+): Animator.AnimatorListener {
+ val listener =
+ object : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animator: Animator) = onRepeat(animator)
+ override fun onAnimationEnd(animator: Animator) = onEnd(animator)
+ override fun onAnimationCancel(animator: Animator) = onCancel(animator)
+ override fun onAnimationStart(animator: Animator) = onStart(animator)
+ }
+ addListener(listener)
+ return listener
+}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 314c736..db88b59 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -171,7 +171,7 @@
a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(4.0)),
+ n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
)),
VIBRANT(CoreSpec(
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
index 26eefa9..377771f 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/dynamiccolor/MaterialDynamicColors.java
@@ -16,6 +16,8 @@
package com.android.systemui.monet.dynamiccolor;
+import android.os.SystemProperties;
+
import com.android.systemui.monet.dislike.DislikeAnalyzer;
import com.android.systemui.monet.dynamiccolor.DynamicColor;
import com.android.systemui.monet.dynamiccolor.ToneDeltaConstraint;
@@ -28,7 +30,8 @@
/** Named colors, otherwise known as tokens, or roles, in the Material Design system. */
public final class MaterialDynamicColors {
private static final double CONTAINER_ACCENT_TONE_DELTA = 15.0;
-
+ private static final boolean IS_FIDELITY_ON_ALL_VARIANTS = SystemProperties.getBoolean(
+ "persist.fidelity_on_theme_variants", false);
private MaterialDynamicColors() {
}
@@ -392,6 +395,9 @@
}
private static boolean isFidelity(DynamicScheme scheme) {
+ if (IS_FIDELITY_ON_ALL_VARIANTS) {
+ return scheme.variant != Variant.NEUTRAL && scheme.variant != Variant.MONOCHROME;
+ }
return scheme.variant == Variant.FIDELITY || scheme.variant == Variant.CONTENT;
}
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index b6a78f5..caf3233 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -82,7 +82,7 @@
<!-- The vertical margin between the date and the owner info. -->
<!-- The translation for disappearing security views after having solved them. -->
- <dimen name="disappear_y_translation">-32dp</dimen>
+ <dimen name="disappear_y_translation">-50dp</dimen>
<!-- Dimens for animation for the Bouncer PIN view -->
<dimen name="pin_view_trans_y_entry">120dp</dimen>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2cedaa7..0e089c8 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1018,7 +1018,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
- <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexion temporaire"</string>
+ <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connectée temporairement"</string>
<string name="mobile_data_poor_connection" msgid="819617772268371434">"Connexion médiocre"</string>
<string name="mobile_data_off_summary" msgid="3663995422004150567">"Pas de connexion automatique des données mobiles"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 44388dc..f62029a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -198,7 +198,8 @@
<skip />
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"அறிவிப்பு விவரம்."</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"உடனடி அமைப்பு."</string>
- <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"விரைவு அமைப்புகளும் அறிவிப்பு விவரமும்."</string>
+ <!-- no translation found for accessibility_desc_qs_notification_shade (8327226953072700376) -->
+ <skip />
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"லாக் ஸ்கிரீன்."</string>
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"பணி லாக் ஸ்கிரீன்"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"மூடு"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8972715..aba3fc4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -573,7 +573,7 @@
<dimen name="qs_tile_margin_horizontal">8dp</dimen>
<dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen>
<dimen name="qs_tile_margin_top_bottom">4dp</dimen>
- <dimen name="qs_brightness_margin_top">12dp</dimen>
+ <dimen name="qs_brightness_margin_top">8dp</dimen>
<dimen name="qs_brightness_margin_bottom">16dp</dimen>
<dimen name="qqs_layout_margin_top">16dp</dimen>
<dimen name="qqs_layout_padding_bottom">24dp</dimen>
@@ -625,7 +625,7 @@
<dimen name="qs_header_row_min_height">48dp</dimen>
<dimen name="qs_header_non_clickable_element_height">24dp</dimen>
- <dimen name="new_qs_header_non_clickable_element_height">20dp</dimen>
+ <dimen name="new_qs_header_non_clickable_element_height">24dp</dimen>
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 6354752..763930d 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -30,9 +30,6 @@
<bool name="flag_charging_ripple">false</bool>
- <!-- Whether to show chipbar UI whenever the device is unlocked by ActiveUnlock. -->
- <bool name="flag_active_unlock_chipbar">true</bool>
-
<!-- Whether the user switcher chip shows in the status bar. When true, the multi user
avatar will no longer show on the lockscreen -->
<bool name="flag_user_switcher_chip">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 558c289..a988813 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -407,8 +407,12 @@
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
- <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string>
+ <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (tablet) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
+ <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the tablet.\n\nPressing the power button turns off the screen.</string>
+ <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (device) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
+ <string name="security_settings_sfps_enroll_find_sensor_message" product="device">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the device.\n\nPressing the power button turns off the screen.</string>
+ <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (default) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
+ <string name="security_settings_sfps_enroll_find_sensor_message" product="default">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the phone.\n\nPressing the power button turns off the screen.</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
<string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f164e7d..c6f0eee 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,7 +39,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
@@ -1044,13 +1043,10 @@
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
- AnimatorSet anims = new AnimatorSet();
ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
- ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
-
- anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
- anims.playTogether(alphaAnim, yAnim);
- anims.start();
+ yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+ yAnim.setDuration(500);
+ yAnim.start();
}
private void setupUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index f1abdc6..06258b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -637,12 +637,17 @@
public void startAppearAnimation() {
if (mCurrentSecurityMode != SecurityMode.None) {
- mView.setAlpha(1f);
+ setAlpha(1f);
mView.startAppearAnimation(mCurrentSecurityMode);
getCurrentSecurityController().startAppearAnimation();
}
}
+ /** Set the alpha of the security container view */
+ public void setAlpha(float alpha) {
+ mView.setAlpha(alpha);
+ }
+
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
boolean didRunAnimation = false;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a4ec8e1..866b502 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -685,7 +685,10 @@
public void onTrustManagedChanged(boolean managed, int userId) {
Assert.isMainThread();
mUserTrustIsManaged.put(userId, managed);
- mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+ mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+ trustUsuallyManaged, "onTrustManagedChanged");
+ mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2393,8 +2396,12 @@
updateSecondaryLockscreenRequirement(user);
List<UserInfo> allUsers = mUserManager.getUsers();
for (UserInfo userInfo : allUsers) {
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userInfo.id);
+ mLogger.logTrustUsuallyManagedUpdated(userInfo.id,
+ mUserTrustIsUsuallyManaged.get(userInfo.id),
+ trustUsuallyManaged, "init from constructor");
mUserTrustIsUsuallyManaged.put(userInfo.id,
- mTrustManager.isTrustUsuallyManaged(userInfo.id));
+ trustUsuallyManaged);
}
updateAirplaneModeState();
@@ -2434,9 +2441,11 @@
}
private void updateFaceEnrolled(int userId) {
- mIsFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
+ Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
&& mBiometricEnabledForUser.get(userId)
&& mAuthController.isFaceAuthEnrolled(userId);
+ mIsFaceEnrolled = isFaceEnrolled;
+ mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
}
public boolean isFaceSupported() {
@@ -3103,9 +3112,13 @@
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
- mIsUnlockWithFingerprintPossible.put(userId, mFpm != null
+ boolean fpEnrolled = mFpm != null
&& !mFingerprintSensorProperties.isEmpty()
- && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+ && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+ mLogger.logFpEnrolledUpdated(userId,
+ mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
+ fpEnrolled);
+ mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
return mIsUnlockWithFingerprintPossible.get(userId);
}
@@ -3221,7 +3234,10 @@
void handleUserSwitching(int userId, CountDownLatch latch) {
Assert.isMainThread();
clearBiometricRecognized();
- mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+ boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+ mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+ trustUsuallyManaged, "userSwitching");
+ mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index e53f6ad..3af9ea3 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -26,6 +26,7 @@
import com.android.keyguard.KeyguardListenModel
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.TrustGrantFlags
+import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -33,18 +34,15 @@
import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.plugins.log.LogLevel.VERBOSE
import com.android.systemui.plugins.log.LogLevel.WARNING
-import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
private const val TAG = "KeyguardUpdateMonitorLog"
-/**
- * Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor]
- */
-class KeyguardUpdateMonitorLogger @Inject constructor(
- @KeyguardUpdateMonitorLog private val logBuffer: LogBuffer
-) {
+/** Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor] */
+class KeyguardUpdateMonitorLogger
+@Inject
+constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
@@ -56,15 +54,16 @@
fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg)
fun logActiveUnlockTriggered(reason: String?) {
- logBuffer.log("ActiveUnlock", DEBUG,
- { str1 = reason },
- { "initiate active unlock triggerReason=$str1" })
+ logBuffer.log(
+ "ActiveUnlock",
+ DEBUG,
+ { str1 = reason },
+ { "initiate active unlock triggerReason=$str1" }
+ )
}
fun logAuthInterruptDetected(active: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = active },
- { "onAuthInterruptDetected($bool1)" })
+ logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
}
fun logBroadcastReceived(action: String?) {
@@ -72,9 +71,12 @@
}
fun logDeviceProvisionedState(deviceProvisioned: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = deviceProvisioned },
- { "DEVICE_PROVISIONED state = $bool1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = deviceProvisioned },
+ { "DEVICE_PROVISIONED state = $bool1" }
+ )
}
fun logException(ex: Exception, @CompileTimeConstant logMsg: String) {
@@ -82,46 +84,56 @@
}
fun logFaceAcquired(acquireInfo: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = acquireInfo },
- { "Face acquired acquireInfo=$int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = acquireInfo }, { "Face acquired acquireInfo=$int1" })
}
fun logFaceAuthDisabledForUser(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Face authentication disabled by DPM for userId: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "Face authentication disabled by DPM for userId: $int1" }
+ )
}
fun logFaceAuthError(msgId: Int, originalErrMsg: String) {
- logBuffer.log(TAG, DEBUG, {
- str1 = originalErrMsg
- int1 = msgId
- }, { "Face error received: $str1 msgId= $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = originalErrMsg
+ int1 = msgId
+ },
+ { "Face error received: $str1 msgId= $int1" }
+ )
}
fun logFaceAuthForWrongUser(authUserId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = authUserId },
- { "Face authenticated for wrong user: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = authUserId },
+ { "Face authenticated for wrong user: $int1" }
+ )
}
fun logFaceAuthHelpMsg(msgId: Int, helpMsg: String?) {
- logBuffer.log(TAG, DEBUG, {
- int1 = msgId
- str1 = helpMsg
- }, { "Face help received, msgId: $int1 msg: $str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = msgId
+ str1 = helpMsg
+ },
+ { "Face help received, msgId: $int1 msg: $str1" }
+ )
}
fun logFaceAuthRequested(reason: String?) {
- logBuffer.log(TAG, DEBUG, {
- str1 = reason
- }, { "requestFaceAuth() reason=$str1" })
+ logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" })
}
fun logFaceAuthSuccess(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Face auth succeeded for user $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" })
}
fun logFaceLockoutReset(@LockoutMode mode: Int) {
@@ -133,21 +145,30 @@
}
fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
- logBuffer.log(TAG, DEBUG,
- { bool1 = isFaceUnlockPossible },
- {"isUnlockWithFacePossible: $bool1"})
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = isFaceUnlockPossible },
+ { "isUnlockWithFacePossible: $bool1" }
+ )
}
fun logFingerprintAuthForWrongUser(authUserId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = authUserId },
- { "Fingerprint authenticated for wrong user: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = authUserId },
+ { "Fingerprint authenticated for wrong user: $int1" }
+ )
}
fun logFingerprintDisabledForUser(userId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = userId },
- { "Fingerprint disabled by DPM for userId: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = userId },
+ { "Fingerprint disabled by DPM for userId: $int1" }
+ )
}
fun logFingerprintLockoutReset(@LockoutMode mode: Int) {
@@ -155,16 +176,24 @@
}
fun logFingerprintRunningState(fingerprintRunningState: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = fingerprintRunningState },
- { "fingerprintRunningState: $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = fingerprintRunningState },
+ { "fingerprintRunningState: $int1" }
+ )
}
fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
- logBuffer.log(TAG, DEBUG, {
- int1 = userId
- bool1 = isStrongBiometric
- }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = isStrongBiometric
+ },
+ { "Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1" }
+ )
}
fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) {
@@ -182,29 +211,42 @@
}
fun logFingerprintError(msgId: Int, originalErrMsg: String) {
- logBuffer.log(TAG, DEBUG, {
- str1 = originalErrMsg
- int1 = msgId
- }, { "Fingerprint error received: $str1 msgId= $int1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = originalErrMsg
+ int1 = msgId
+ },
+ { "Fingerprint error received: $str1 msgId= $int1" }
+ )
}
fun logInvalidSubId(subId: Int) {
- logBuffer.log(TAG, INFO,
- { int1 = subId },
- { "Previously active sub id $int1 is now invalid, will remove" })
+ logBuffer.log(
+ TAG,
+ INFO,
+ { int1 = subId },
+ { "Previously active sub id $int1 is now invalid, will remove" }
+ )
}
fun logPrimaryKeyguardBouncerChanged(
- primaryBouncerIsOrWillBeShowing: Boolean,
- primaryBouncerFullyShown: Boolean
+ primaryBouncerIsOrWillBeShowing: Boolean,
+ primaryBouncerFullyShown: Boolean
) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = primaryBouncerIsOrWillBeShowing
- bool2 = primaryBouncerFullyShown
- }, {
- "handlePrimaryBouncerChanged " +
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
+ },
+ {
+ "handlePrimaryBouncerChanged " +
"primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
- })
+ }
+ )
}
fun logKeyguardListenerModel(model: KeyguardListenModel) {
@@ -212,98 +254,134 @@
}
fun logKeyguardShowingChanged(showing: Boolean, occluded: Boolean, visible: Boolean) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = showing
- bool2 = occluded
- bool3 = visible
- }, {
- "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)"
- })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = showing
+ bool2 = occluded
+ bool3 = visible
+ },
+ { "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)" }
+ )
}
fun logMissingSupervisorAppError(userId: Int) {
- logBuffer.log(TAG, ERROR,
- { int1 = userId },
- { "No Profile Owner or Device Owner supervision app found for User $int1" })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ { int1 = userId },
+ { "No Profile Owner or Device Owner supervision app found for User $int1" }
+ )
}
fun logPhoneStateChanged(newState: String?) {
- logBuffer.log(TAG, DEBUG,
- { str1 = newState },
- { "handlePhoneStateChanged($str1)" })
+ logBuffer.log(TAG, DEBUG, { str1 = newState }, { "handlePhoneStateChanged($str1)" })
}
fun logRegisterCallback(callback: KeyguardUpdateMonitorCallback?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$callback" },
- { "*** register callback for $str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" })
}
fun logRetryingAfterFaceHwUnavailable(retryCount: Int) {
- logBuffer.log(TAG, WARNING,
- { int1 = retryCount },
- { "Retrying face after HW unavailable, attempt $int1" })
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { int1 = retryCount },
+ { "Retrying face after HW unavailable, attempt $int1" }
+ )
}
fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
- logBuffer.log(TAG, DEBUG, {
- int1 = msgId
- int2 = delay
- str1 = "$errString"
- }, {
- "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1"
- })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = msgId
+ int2 = delay
+ str1 = "$errString"
+ },
+ { "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1" }
+ )
}
fun logRetryAfterFpHwUnavailable(retryCount: Int) {
- logBuffer.log(TAG, WARNING,
- { int1 = retryCount },
- { "Retrying fingerprint attempt: $int1" })
+ logBuffer.log(
+ TAG,
+ WARNING,
+ { int1 = retryCount },
+ { "Retrying fingerprint attempt: $int1" }
+ )
}
fun logSendPrimaryBouncerChanged(
primaryBouncerIsOrWillBeShowing: Boolean,
primaryBouncerFullyShown: Boolean,
) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = primaryBouncerIsOrWillBeShowing
- bool2 = primaryBouncerFullyShown
- }, {
- "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = primaryBouncerIsOrWillBeShowing
+ bool2 = primaryBouncerFullyShown
+ },
+ {
+ "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
"primaryBouncerFullyShown=$bool2"
- })
+ }
+ )
}
fun logServiceStateChange(subId: Int, serviceState: ServiceState?) {
- logBuffer.log(TAG, DEBUG, {
- int1 = subId
- str1 = "$serviceState"
- }, { "handleServiceStateChange(subId=$int1, serviceState=$str1)" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = subId
+ str1 = "$serviceState"
+ },
+ { "handleServiceStateChange(subId=$int1, serviceState=$str1)" }
+ )
}
fun logServiceStateIntent(action: String?, serviceState: ServiceState?, subId: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = action
- str2 = "$serviceState"
- int1 = subId
- }, { "action $str1 serviceState=$str2 subId=$int1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ str1 = action
+ str2 = "$serviceState"
+ int1 = subId
+ },
+ { "action $str1 serviceState=$str2 subId=$int1" }
+ )
}
fun logSimState(subId: Int, slotId: Int, state: Int) {
- logBuffer.log(TAG, DEBUG, {
- int1 = subId
- int2 = slotId
- long1 = state.toLong()
- }, { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = subId
+ int2 = slotId
+ long1 = state.toLong()
+ },
+ { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }
+ )
}
fun logSimStateFromIntent(action: String?, extraSimState: String?, slotId: Int, subId: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = action
- str2 = extraSimState
- int1 = slotId
- int2 = subId
- }, { "action $str1 state: $str2 slotId: $int1 subid: $int2" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ str1 = action
+ str2 = extraSimState
+ int1 = slotId
+ int2 = subId
+ },
+ { "action $str1 state: $str2 slotId: $int1 subid: $int2" }
+ )
}
fun logSimUnlocked(subId: Int) {
@@ -311,78 +389,98 @@
}
fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = faceAuthUiEvent.reason
- str2 = faceAuthUiEvent.extraInfoToString()
- }, { "startListeningForFace(): $int1, reason: $str1 $str2" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = faceAuthUiEvent.reason
+ str2 = faceAuthUiEvent.extraInfoToString()
+ },
+ { "startListeningForFace(): $int1, reason: $str1 $str2" }
+ )
}
fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = PowerManager.wakeReasonToString(pmWakeReason)
- }, { "startListeningForFace(): $int1, reason: wakeUp-$str1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = PowerManager.wakeReasonToString(pmWakeReason)
+ },
+ { "startListeningForFace(): $int1, reason: wakeUp-$str1" }
+ )
}
fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
- logBuffer.log(TAG, VERBOSE, {
- int1 = faceRunningState
- str1 = faceAuthReason
- }, { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = faceRunningState
+ str1 = faceAuthReason
+ },
+ { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" }
+ )
}
fun logSubInfo(subInfo: SubscriptionInfo?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$subInfo" },
- { "SubInfo:$str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$subInfo" }, { "SubInfo:$str1" })
}
fun logTimeFormatChanged(newTimeFormat: String?) {
- logBuffer.log(TAG, DEBUG,
- { str1 = newTimeFormat },
- { "handleTimeFormatUpdate timeFormat=$str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = newTimeFormat },
+ { "handleTimeFormatUpdate timeFormat=$str1" }
+ )
}
fun logUdfpsPointerDown(sensorId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = sensorId },
- { "onUdfpsPointerDown, sensorId: $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerDown, sensorId: $int1" })
}
fun logUdfpsPointerUp(sensorId: Int) {
- logBuffer.log(TAG, DEBUG,
- { int1 = sensorId },
- { "onUdfpsPointerUp, sensorId: $int1" })
+ logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" })
}
fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) {
- logBuffer.log(TAG, ERROR, {
- int1 = faceRunningState
- bool1 = unlockPossible
- }, {
- "Cancellation signal is not null, high chance of bug in " +
- "face auth lifecycle management. " +
- "Face state: $int1, unlockPossible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ {
+ int1 = faceRunningState
+ bool1 = unlockPossible
+ },
+ {
+ "Cancellation signal is not null, high chance of bug in " +
+ "face auth lifecycle management. " +
+ "Face state: $int1, unlockPossible: $bool1"
+ }
+ )
}
fun logUnexpectedFpCancellationSignalState(
fingerprintRunningState: Int,
unlockPossible: Boolean
) {
- logBuffer.log(TAG, ERROR, {
- int1 = fingerprintRunningState
- bool1 = unlockPossible
- }, {
- "Cancellation signal is not null, high chance of bug in " +
- "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ ERROR,
+ {
+ int1 = fingerprintRunningState
+ bool1 = unlockPossible
+ },
+ {
+ "Cancellation signal is not null, high chance of bug in " +
+ "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
+ }
+ )
}
fun logUnregisterCallback(callback: KeyguardUpdateMonitorCallback?) {
- logBuffer.log(TAG, VERBOSE,
- { str1 = "$callback" },
- { "*** unregister callback for $str1" })
+ logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** unregister callback for $str1" })
}
fun logUserRequestedUnlock(
@@ -390,75 +488,149 @@
reason: String?,
dismissKeyguard: Boolean
) {
- logBuffer.log("ActiveUnlock", DEBUG, {
- str1 = requestOrigin?.name
- str2 = reason
- bool1 = dismissKeyguard
- }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
+ logBuffer.log(
+ "ActiveUnlock",
+ DEBUG,
+ {
+ str1 = requestOrigin?.name
+ str2 = reason
+ bool1 = dismissKeyguard
+ },
+ { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" }
+ )
}
fun logTrustGrantedWithFlags(
- flags: Int,
- newlyUnlocked: Boolean,
- userId: Int,
- message: String?
+ flags: Int,
+ newlyUnlocked: Boolean,
+ userId: Int,
+ message: String?
) {
- logBuffer.log(TAG, DEBUG, {
- int1 = flags
- bool1 = newlyUnlocked
- int2 = userId
- str1 = message
- }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
- "flags=${TrustGrantFlags(int1)} message=$str1" })
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = flags
+ bool1 = newlyUnlocked
+ int2 = userId
+ str1 = message
+ },
+ {
+ "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
+ "flags=${TrustGrantFlags(int1)} message=$str1"
+ }
+ )
}
- fun logTrustChanged(
- wasTrusted: Boolean,
- isNowTrusted: Boolean,
- userId: Int
- ) {
- logBuffer.log(TAG, DEBUG, {
- bool1 = wasTrusted
- bool2 = isNowTrusted
- int1 = userId
- }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+ fun logTrustChanged(wasTrusted: Boolean, isNowTrusted: Boolean, userId: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = wasTrusted
+ bool2 = isNowTrusted
+ int1 = userId
+ },
+ { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" }
+ )
}
fun logKeyguardStateUpdate(
- secure: Boolean,
- canDismissLockScreen: Boolean,
- trusted: Boolean,
- trustManaged: Boolean
-
+ secure: Boolean,
+ canDismissLockScreen: Boolean,
+ trusted: Boolean,
+ trustManaged: Boolean
) {
- logBuffer.log("KeyguardState", DEBUG, {
- bool1 = secure
- bool2 = canDismissLockScreen
- bool3 = trusted
- bool4 = trustManaged
- }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
- " trusted=$bool3 trustManaged=$bool4" })
+ logBuffer.log(
+ "KeyguardState",
+ DEBUG,
+ {
+ bool1 = secure
+ bool2 = canDismissLockScreen
+ bool3 = trusted
+ bool4 = trustManaged
+ },
+ {
+ "#update secure=$bool1 canDismissKeyguard=$bool2" +
+ " trusted=$bool3 trustManaged=$bool4"
+ }
+ )
}
fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) {
- logBuffer.log(TAG, VERBOSE, {
- str1 = PowerManager.wakeReasonToString(pmWakeReason)
- }, { "Skip updating face listening state on wakeup from $str1"})
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { str1 = PowerManager.wakeReasonToString(pmWakeReason) },
+ { "Skip updating face listening state on wakeup from $str1" }
+ )
}
fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
- logBuffer.log(TAG, VERBOSE, {
- bool1 = assistantVisible
- }, {
- "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { bool1 = assistantVisible },
+ { "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1" }
+ )
}
fun logAssistantVisible(assistantVisible: Boolean) {
- logBuffer.log(TAG, VERBOSE, {
- bool1 = assistantVisible
- }, {
- "Updating mAssistantVisible to new value: $bool1"
- })
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { bool1 = assistantVisible },
+ { "Updating mAssistantVisible to new value: $bool1" }
+ )
+ }
+
+ fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = oldValue
+ bool2 = newValue
+ },
+ { "Face enrolled state changed: old: $bool1, new: $bool2" }
+ )
+ }
+
+ fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = oldValue
+ bool2 = newValue
+ },
+ { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
+ )
+ }
+
+ fun logTrustUsuallyManagedUpdated(
+ userId: Int,
+ oldValue: Boolean,
+ newValue: Boolean,
+ context: String
+ ) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ int1 = userId
+ bool1 = oldValue
+ bool2 = newValue
+ str1 = context
+ },
+ {
+ "trustUsuallyManaged changed for " +
+ "userId: $int1 " +
+ "old: $bool1, " +
+ "new: $bool2 " +
+ "context: $str1"
+ }
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..1f66c91
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 44215
+
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index ce42534..0002ae9 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -38,7 +38,6 @@
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -347,10 +346,9 @@
}
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
updatePercentText();
- addView(mBatteryPercentView,
- new ViewGroup.LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT));
+ addView(mBatteryPercentView, new LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT));
}
} else {
if (showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 709ddf5..52312b8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -97,7 +97,6 @@
val iconContentDescription = getIconContentDescription(newState)
if (iconContentDescription != null) {
iconView.contentDescription = iconContentDescription
- iconViewOverlay.contentDescription = iconContentDescription
}
iconView.frame = 0
@@ -152,7 +151,7 @@
STATE_AUTHENTICATING_ANIMATING_IN,
STATE_AUTHENTICATING,
STATE_PENDING_CONFIRMATION,
- STATE_AUTHENTICATED -> R.string.accessibility_fingerprint_dialog_fingerprint_icon
+ STATE_AUTHENTICATED -> R.string.security_settings_sfps_enroll_find_sensor_message
STATE_ERROR,
STATE_HELP -> R.string.biometric_dialog_try_again
else -> null
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 72c3a94..217f4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -84,7 +84,7 @@
private val BIND_FLAGS_PANEL = Context.BIND_AUTO_CREATE or Context.BIND_NOT_PERCEPTIBLE
}
- private val intent = Intent().apply {
+ private val intent = Intent(ControlsProviderService.SERVICE_CONTROLS).apply {
component = componentName
putExtra(CALLBACK_BUNDLE, Bundle().apply {
putBinder(CALLBACK_TOKEN, token)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b21df06..9264e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -151,12 +151,6 @@
val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES =
releasedFlag(216, "customizable_lock_screen_quick_affordances")
- /** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
- // TODO(b/256513609): Tracking Bug
- @JvmField
- val ACTIVE_UNLOCK_CHIPBAR =
- resourceBooleanFlag(217, R.bool.flag_active_unlock_chipbar, "active_unlock_chipbar")
-
/**
* Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
* new KeyguardTransitionRepository.
@@ -263,8 +257,7 @@
/** Enables new QS Edit Mode visual refresh */
// TODO(b/269787742): Tracking Bug
@JvmField
- val ENABLE_NEW_QS_EDIT_MODE =
- unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false)
+ val ENABLE_NEW_QS_EDIT_MODE = unreleasedFlag(510, "enable_new_qs_edit_mode", teamfood = false)
// 600- status bar
@@ -376,6 +369,9 @@
// TODO(b/254512758): Tracking Bug
@JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple")
+ // TODO(b/270882464): Tracking Bug
+ val ENABLE_DOCK_SETUP_V2 = unreleasedFlag(1005, "enable_dock_setup_v2")
+
// TODO(b/265045965): Tracking Bug
val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
@@ -597,8 +593,7 @@
@JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
// 2300 - stylus
- @JvmField
- val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
+ @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
@JvmField
val ENABLE_STYLUS_CHARGING_UI =
unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
@@ -622,11 +617,6 @@
@JvmField
val OUTPUT_SWITCHER_DEVICE_STATUS = releasedFlag(2502, "output_switcher_device_status")
- // TODO(b/20911786): Tracking Bug
- @JvmField
- val OUTPUT_SWITCHER_SHOW_API_ENABLED =
- releasedFlag(2503, "output_switcher_show_api_enabled", teamfood = true)
-
// 2700 - unfold transitions
// TODO(b/265764985): Tracking Bug
@Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a3b3d0f..76f20d25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -80,6 +80,9 @@
*/
val isKeyguardShowing: Flow<Boolean>
+ /** Is the keyguard in a unlocked state? */
+ val isKeyguardUnlocked: Flow<Boolean>
+
/** Is an activity showing over the keyguard? */
val isKeyguardOccluded: Flow<Boolean>
@@ -278,6 +281,31 @@
}
.distinctUntilChanged()
+ override val isKeyguardUnlocked: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "updated isKeyguardUnlocked"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "initial isKeyguardUnlocked"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 0c4bca6..100bc59 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -68,8 +68,11 @@
/**
* Begin a transition from one state to another. Transitions are interruptible, and will issue a
* [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
+ *
+ * When canceled, there are two options: to continue from the current position of the prior
+ * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
*/
- fun startTransition(info: TransitionInfo): UUID?
+ fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
/**
* Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -130,7 +133,10 @@
)
}
- override fun startTransition(info: TransitionInfo): UUID? {
+ override fun startTransition(
+ info: TransitionInfo,
+ resetIfCanceled: Boolean,
+ ): UUID? {
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return null
@@ -138,7 +144,11 @@
val startingValue =
if (lastStep.transitionState != TransitionState.FINISHED) {
Log.i(TAG, "Transition still active: $lastStep, canceling")
- lastStep.value
+ if (resetIfCanceled) {
+ 0f
+ } else {
+ lastStep.value
+ }
} else {
0f
}
@@ -227,10 +237,7 @@
}
private fun trace(step: TransitionStep, isManual: Boolean) {
- if (
- step.transitionState != TransitionState.STARTED &&
- step.transitionState != TransitionState.FINISHED
- ) {
+ if (step.transitionState == TransitionState.RUNNING) {
return
}
val traceName =
@@ -243,7 +250,10 @@
val traceCookie = traceName.hashCode()
if (step.transitionState == TransitionState.STARTED) {
Trace.beginAsyncSection(traceName, traceCookie)
- } else if (step.transitionState == TransitionState.FINISHED) {
+ } else if (
+ step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED
+ ) {
Trace.endAsyncSection(traceName, traceCookie)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 8715d1f..3beac0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -34,7 +34,6 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -57,14 +56,7 @@
private fun listenForDreamingToLockscreen() {
scope.launch {
- // Dependending on the dream, either dream state or occluded change will change first,
- // so listen for both
- combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
- isAbleToDream,
- isKeyguardOccluded ->
- isAbleToDream && isKeyguardOccluded
- }
- .distinctUntilChanged()
+ keyguardInteractor.isAbleToDream
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index d01f489..911861d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -130,55 +130,59 @@
shadeRepository.shadeModel
.sample(
combine(
- keyguardTransitionInteractor.finishedKeyguardState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.statusBarState,
- ::Pair
+ keyguardInteractor.isKeyguardUnlocked,
+ ::toTriple
),
- ::toTriple
+ ::toQuad
)
- .collect { (shadeModel, keyguardState, statusBarState) ->
+ .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
val id = transitionId
if (id != null) {
- // An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED or CANCELED
- var nextState =
- if (shadeModel.expansionAmount == 0f) {
- TransitionState.FINISHED
- } else if (shadeModel.expansionAmount == 1f) {
- TransitionState.CANCELED
- } else {
- TransitionState.RUNNING
- }
- keyguardTransitionRepository.updateTransition(
- id,
- 1f - shadeModel.expansionAmount,
- nextState,
- )
-
- if (
- nextState == TransitionState.CANCELED ||
- nextState == TransitionState.FINISHED
- ) {
- transitionId = null
- }
-
- // If canceled, just put the state back
- if (nextState == TransitionState.CANCELED) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- ownerName = name,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN,
- animator = getAnimator(0.milliseconds)
- )
+ if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
+ // An existing `id` means a transition is started, and calls to
+ // `updateTransition` will control it until FINISHED or CANCELED
+ var nextState =
+ if (shadeModel.expansionAmount == 0f) {
+ TransitionState.FINISHED
+ } else if (shadeModel.expansionAmount == 1f) {
+ TransitionState.CANCELED
+ } else {
+ TransitionState.RUNNING
+ }
+ keyguardTransitionRepository.updateTransition(
+ id,
+ 1f - shadeModel.expansionAmount,
+ nextState,
)
+
+ if (
+ nextState == TransitionState.CANCELED ||
+ nextState == TransitionState.FINISHED
+ ) {
+ transitionId = null
+ }
+
+ // If canceled, just put the state back
+ if (nextState == TransitionState.CANCELED) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animator = getAnimator(0.milliseconds)
+ )
+ )
+ }
}
} else {
// TODO (b/251849525): Remove statusbarstate check when that state is
// integrated into KeyguardTransitionRepository
if (
- keyguardState == KeyguardState.LOCKSCREEN &&
+ keyguardState.to == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging &&
+ !isKeyguardUnlocked &&
statusBarState == KEYGUARD
) {
transitionId =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index b59b413..94961cb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -17,6 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -26,6 +29,8 @@
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -37,7 +42,8 @@
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
- private val keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardSecurityModel: KeyguardSecurityModel,
) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
override fun start() {
@@ -93,31 +99,47 @@
private fun listenForPrimaryBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
- .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
- .collect { pair ->
- val (isKeyguardGoingAway, keyguardState) = pair
- if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
+ .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
+ if (
+ isKeyguardGoingAway &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+ ) {
+ val securityMode =
+ keyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()
+ )
+ // IME for password requires a slightly faster animation
+ val duration =
+ if (securityMode == Password) {
+ TO_GONE_SHORT_DURATION
+ } else {
+ TO_GONE_DURATION
+ }
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
- animator = getAnimator(),
- )
+ animator = getAnimator(duration),
+ ),
+ resetIfCanceled = true,
)
}
}
}
}
- private fun getAnimator(): ValueAnimator {
+ private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
- setDuration(TRANSITION_DURATION_MS)
+ setDuration(duration.inWholeMilliseconds)
}
}
companion object {
- private const val TRANSITION_DURATION_MS = 300L
+ private val DEFAULT_DURATION = 300.milliseconds
+ val TO_GONE_DURATION = 250.milliseconds
+ val TO_GONE_SHORT_DURATION = 200.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index d25aff0a..ec99049 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -33,7 +33,9 @@
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -95,6 +97,9 @@
awaitClose { commandQueue.removeCallback(callback) }
}
+ /** The device wake/sleep state */
+ val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
+
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
@@ -109,6 +114,12 @@
isDreaming && isDozeOff(dozeTransitionModel.to)
}
)
+ .sample(
+ wakefulnessModel,
+ { isAbleToDream, wakefulnessModel ->
+ isAbleToDream && isWakingOrStartingToWake(wakefulnessModel)
+ }
+ )
.flatMapLatest { isAbleToDream ->
flow {
delay(50)
@@ -119,6 +130,8 @@
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** Whether the keyguard is unlocked or not. */
+ val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */
@@ -127,8 +140,6 @@
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
- /** The device wake/sleep state */
- val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 51b0277..e650b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -61,7 +61,15 @@
}
scope.launch {
- keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) }
+ keyguardInteractor.isAbleToDream.collect {
+ logger.log(TAG, VERBOSE, "isAbleToDream", it)
+ }
+ }
+
+ scope.launch {
+ keyguardInteractor.isKeyguardOccluded.collect {
+ logger.log(TAG, VERBOSE, "isOccluded", it)
+ }
}
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 1b7da5b..3c0ec35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -78,6 +78,10 @@
val occludedToLockscreenTransition: Flow<TransitionStep> =
repository.transition(OCCLUDED, LOCKSCREEN)
+ /** PRIMARY_BOUNCER->GONE transition information. */
+ val primaryBouncerToGoneTransition: Flow<TransitionStep> =
+ repository.transition(PRIMARY_BOUNCER, GONE)
+
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index edd2897..7368a46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -21,8 +21,10 @@
import android.hardware.biometrics.BiometricSourceType
import android.os.Handler
import android.os.Trace
-import android.view.View
+import android.os.UserHandle
+import android.os.UserManager
import android.util.Log
+import android.view.View
import com.android.keyguard.KeyguardConstants
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index ab009f4..2a9060f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -342,7 +342,13 @@
if (viewModel.isClickable) {
if (viewModel.useLongPress) {
view.setOnTouchListener(
- OnTouchListener(view, viewModel, messageDisplayer, vibratorHelper)
+ OnTouchListener(
+ view,
+ viewModel,
+ messageDisplayer,
+ vibratorHelper,
+ falsingManager,
+ )
)
} else {
view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
@@ -371,6 +377,7 @@
private val viewModel: KeyguardQuickAffordanceViewModel,
private val messageDisplayer: (Int) -> Unit,
private val vibratorHelper: VibratorHelper?,
+ private val falsingManager: FalsingManager?,
) : View.OnTouchListener {
private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong()
@@ -395,7 +402,14 @@
.scaleY(PRESSED_SCALE)
.setDuration(longPressDurationMs)
.withEndAction {
- dispatchClick(viewModel.configKey)
+ if (
+ falsingManager
+ ?.isFalseLongTap(
+ FalsingManager.MODERATE_PENALTY
+ ) == false
+ ) {
+ dispatchClick(viewModel.configKey)
+ }
cancel()
}
}
@@ -421,7 +435,8 @@
// the pointer performs a click.
if (
viewModel.configKey != null &&
- distanceMoved(event) <= ViewConfiguration.getTouchSlop()
+ distanceMoved(event) <= ViewConfiguration.getTouchSlop() &&
+ falsingManager?.isFalseTap(FalsingManager.NO_PENALTY) == false
) {
dispatchClick(viewModel.configKey)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 7db567b..2337ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -31,6 +31,7 @@
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
import kotlinx.coroutines.awaitCancellation
@@ -44,6 +45,7 @@
fun bind(
view: ViewGroup,
viewModel: KeyguardBouncerViewModel,
+ primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
componentFactory: KeyguardBouncerComponent.Factory
) {
// Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -145,6 +147,12 @@
}
launch {
+ primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
+ securityContainerController.setAlpha(alpha)
+ }
+ }
+
+ launch {
viewModel.bouncerExpansionAmount
.filter { it == EXPANSION_VISIBLE }
.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..0890791
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@SysUISingleton
+class PrimaryBouncerToGoneTransitionViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+) {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_GONE_DURATION,
+ transitionFlow = interactor.primaryBouncerToGoneTransition,
+ )
+
+ /** Bouncer container alpha */
+ val bouncerAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ )
+
+ /** Scrim alpha */
+ val scrimAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = TO_GONE_DURATION,
+ interpolator = EMPHASIZED_ACCELERATE,
+ onStep = { 1f - it },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 00e9a79..04150a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,17 +16,16 @@
package com.android.systemui.media.dialog;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER;
import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_GO_TO_APP;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -296,6 +295,8 @@
&& mController.isAdvancedLayoutSupported()) {
//If device is connected and there's other selectable devices, layout as
// one of selected devices.
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
boolean isDeviceDeselectable = isDeviceIncluded(
mController.getDeselectableMediaDevice(), device);
updateGroupableCheckBox(true, isDeviceDeselectable, device);
@@ -343,7 +344,7 @@
updateDeviceStatusIcon(deviceStatusIcon);
mStatusIcon.setVisibility(View.VISIBLE);
}
- updateTwoLineLayoutContentAlpha(
+ updateSingleLineLayoutContentAlpha(
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
} else {
@@ -367,11 +368,18 @@
mStatusIcon.setAlpha(alphaValue);
}
+ private void updateSingleLineLayoutContentAlpha(float alphaValue) {
+ mTitleIcon.setAlpha(alphaValue);
+ mTitleText.setAlpha(alphaValue);
+ mStatusIcon.setAlpha(alphaValue);
+ }
+
private void updateEndClickAreaAsSessionEditing(MediaDevice device) {
mEndClickIcon.setOnClickListener(null);
mEndTouchArea.setOnClickListener(null);
updateEndClickAreaColor(mController.getColorSeekbarProgress());
- mEndClickIcon.setColorFilter(mController.getColorItemContent());
+ mEndClickIcon.setImageTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
mEndTouchArea.setOnClickListener(v -> mCheckBox.performClick());
@@ -379,8 +387,8 @@
public void updateEndClickAreaColor(int color) {
if (mController.isAdvancedLayoutSupported()) {
- mEndTouchArea.getBackground().setColorFilter(
- new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
+ mEndTouchArea.setBackgroundTintList(
+ ColorStateList.valueOf(color));
}
}
@@ -394,22 +402,22 @@
private void updateConnectionFailedStatusIcon() {
mStatusIcon.setImageDrawable(
mContext.getDrawable(R.drawable.media_output_status_failed));
- mStatusIcon.setColorFilter(mController.getColorItemContent());
+ mStatusIcon.setImageTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
}
private void updateDeviceStatusIcon(Drawable drawable) {
mStatusIcon.setImageDrawable(drawable);
- mStatusIcon.setColorFilter(mController.getColorItemContent());
+ mStatusIcon.setImageTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
if (drawable instanceof AnimatedVectorDrawable) {
((AnimatedVectorDrawable) drawable).start();
}
}
private void updateProgressBarColor() {
- mProgressBar.getIndeterminateDrawable().setColorFilter(
- new PorterDuffColorFilter(
- mController.getColorItemContent(),
- PorterDuff.Mode.SRC_IN));
+ mProgressBar.getIndeterminateDrawable().setTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
}
public void updateEndClickArea(MediaDevice device, boolean isDeviceDeselectable) {
@@ -419,9 +427,8 @@
mEndTouchArea.setImportantForAccessibility(
View.IMPORTANT_FOR_ACCESSIBILITY_YES);
if (mController.isAdvancedLayoutSupported()) {
- mEndTouchArea.getBackground().setColorFilter(
- new PorterDuffColorFilter(mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ mEndTouchArea.setBackgroundTintList(
+ ColorStateList.valueOf(mController.getColorItemBackground()));
}
setUpContentDescriptionForView(mEndTouchArea, true, device);
}
@@ -450,11 +457,11 @@
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new));
final Drawable addDrawable = mContext.getDrawable(R.drawable.ic_add);
mTitleIcon.setImageDrawable(addDrawable);
- mTitleIcon.setColorFilter(mController.getColorItemContent());
+ mTitleIcon.setImageTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
if (mController.isAdvancedLayoutSupported()) {
- mIconAreaLayout.getBackground().setColorFilter(
- new PorterDuffColorFilter(mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ mIconAreaLayout.setBackgroundTintList(
+ ColorStateList.valueOf(mController.getColorItemBackground()));
}
mContainerLayout.setOnClickListener(mController::launchBluetoothPairing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 2a2cf63..f76f049 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -23,8 +23,7 @@
import android.annotation.DrawableRes;
import android.app.WallpaperColors;
import android.content.Context;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
+import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
@@ -196,9 +195,8 @@
mIconAreaLayout.setOnClickListener(null);
mVolumeValueText.setTextColor(mController.getColorItemContent());
}
- mSeekBar.getProgressDrawable().setColorFilter(
- new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
- PorterDuff.Mode.SRC_IN));
+ mSeekBar.setProgressTintList(
+ ColorStateList.valueOf(mController.getColorSeekbarProgress()));
}
abstract void onBind(int customizedItem);
@@ -224,16 +222,14 @@
updateSeekbarProgressBackground();
}
}
- mItemLayout.getBackground().setColorFilter(new PorterDuffColorFilter(
- isActive ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ mItemLayout.setBackgroundTintList(
+ ColorStateList.valueOf(isActive ? mController.getColorConnectedItemBackground()
+ : mController.getColorItemBackground()));
if (mController.isAdvancedLayoutSupported()) {
- mIconAreaLayout.getBackground().setColorFilter(new PorterDuffColorFilter(
- showSeekBar ? mController.getColorSeekbarProgress()
+ mIconAreaLayout.setBackgroundTintList(
+ ColorStateList.valueOf(showSeekBar ? mController.getColorSeekbarProgress()
: showProgressBar ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ : mController.getColorItemBackground()));
}
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSeekBar.setAlpha(1);
@@ -251,7 +247,8 @@
params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
: mController.getItemMarginEndDefault();
}
- mTitleIcon.setColorFilter(mController.getColorItemContent());
+ mTitleIcon.setBackgroundTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
}
void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
@@ -274,15 +271,14 @@
backgroundDrawable = mContext.getDrawable(
showSeekBar ? R.drawable.media_output_item_background_active
: R.drawable.media_output_item_background).mutate();
- backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
+ backgroundDrawable.setTint(
showSeekBar ? mController.getColorConnectedItemBackground()
- : mController.getColorItemBackground(), PorterDuff.Mode.SRC_IN));
- mIconAreaLayout.getBackground().setColorFilter(new PorterDuffColorFilter(
- showProgressBar || isFakeActive
+ : mController.getColorItemBackground());
+ mIconAreaLayout.setBackgroundTintList(
+ ColorStateList.valueOf(showProgressBar || isFakeActive
? mController.getColorConnectedItemBackground()
: showSeekBar ? mController.getColorSeekbarProgress()
- : mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ : mController.getColorItemBackground()));
if (showSeekBar) {
updateSeekbarProgressBackground();
}
@@ -297,9 +293,7 @@
backgroundDrawable = mContext.getDrawable(
R.drawable.media_output_item_background)
.mutate();
- backgroundDrawable.setColorFilter(new PorterDuffColorFilter(
- mController.getColorItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ backgroundDrawable.setTint(mController.getColorItemBackground());
}
mItemLayout.setBackground(backgroundDrawable);
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
@@ -442,11 +436,10 @@
void updateTitleIcon(@DrawableRes int id, int color) {
mTitleIcon.setImageDrawable(mContext.getDrawable(id));
- mTitleIcon.setColorFilter(color);
+ mTitleIcon.setImageTintList(ColorStateList.valueOf(color));
if (mController.isAdvancedLayoutSupported()) {
- mIconAreaLayout.getBackground().setColorFilter(
- new PorterDuffColorFilter(mController.getColorSeekbarProgress(),
- PorterDuff.Mode.SRC_IN));
+ mIconAreaLayout.setBackgroundTintList(
+ ColorStateList.valueOf(mController.getColorSeekbarProgress()));
}
}
@@ -462,9 +455,7 @@
final Drawable backgroundDrawable = mContext.getDrawable(
R.drawable.media_output_item_background_active)
.mutate();
- backgroundDrawable.setColorFilter(
- new PorterDuffColorFilter(mController.getColorConnectedItemBackground(),
- PorterDuff.Mode.SRC_IN));
+ backgroundDrawable.setTint(mController.getColorConnectedItemBackground());
mItemLayout.setBackground(backgroundDrawable);
}
@@ -539,10 +530,8 @@
Drawable getSpeakerDrawable() {
final Drawable drawable = mContext.getDrawable(R.drawable.ic_speaker_group_black_24dp)
.mutate();
- drawable.setColorFilter(
- new PorterDuffColorFilter(Utils.getColorStateListDefaultColor(mContext,
- R.color.media_dialog_item_main_content),
- PorterDuff.Mode.SRC_IN));
+ drawable.setTint(Utils.getColorStateListDefaultColor(mContext,
+ R.color.media_dialog_item_main_content));
return drawable;
}
@@ -574,7 +563,9 @@
return;
}
mTitleIcon.setImageIcon(icon);
- mTitleIcon.setColorFilter(mController.getColorItemContent());
+ icon.setTint(mController.getColorItemContent());
+ mTitleIcon.setImageTintList(
+ ColorStateList.valueOf(mController.getColorItemContent()));
});
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 2aedd36..9203897 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -395,7 +395,6 @@
launchIntent.putExtra(EXTRA_ROUTE_ID, routeId);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCallback.dismissDialog();
- mContext.startActivity(launchIntent);
mActivityStarter.startActivity(launchIntent, true, controller);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 2250d72..39d4e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -80,6 +80,10 @@
Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
}
+ if (mSourceDevice == null && mTargetDevice == null) {
+ return;
+ }
+
updateLoggingDeviceCount(deviceList);
SysUiStatsLog.write(
@@ -105,6 +109,10 @@
Log.d(TAG, "logOutputSuccess - selected device: " + selectedDeviceType);
}
+ if (mSourceDevice == null && mTargetDevice == null) {
+ return;
+ }
+
updateLoggingMediaItemCount(deviceItemList);
SysUiStatsLog.write(
@@ -176,6 +184,10 @@
Log.e(TAG, "logRequestFailed - " + reason);
}
+ if (mSourceDevice == null && mTargetDevice == null) {
+ return;
+ }
+
updateLoggingDeviceCount(deviceList);
SysUiStatsLog.write(
@@ -201,6 +213,10 @@
Log.e(TAG, "logRequestFailed - " + reason);
}
+ if (mSourceDevice == null && mTargetDevice == null) {
+ return;
+ }
+
updateLoggingMediaItemCount(deviceItemList);
SysUiStatsLog.write(
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
index e35575b..b5b1f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSwitcherDialogUI.java
@@ -23,8 +23,6 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.CommandQueue;
import javax.inject.Inject;
@@ -37,26 +35,19 @@
private final CommandQueue mCommandQueue;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
- private final FeatureFlags mFeatureFlags;
@Inject
public MediaOutputSwitcherDialogUI(
Context context,
CommandQueue commandQueue,
- MediaOutputDialogFactory mediaOutputDialogFactory,
- FeatureFlags featureFlags) {
+ MediaOutputDialogFactory mediaOutputDialogFactory) {
mCommandQueue = commandQueue;
mMediaOutputDialogFactory = mediaOutputDialogFactory;
- mFeatureFlags = featureFlags;
}
@Override
public void start() {
- if (mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_SHOW_API_ENABLED)) {
- mCommandQueue.addCallback(this);
- } else {
- Log.w(TAG, "Show media output switcher is not enabled.");
- }
+ mCommandQueue.addCallback(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index e86bd7a..9f93e49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -76,7 +76,8 @@
this(tileServices, handler, userTracker, new TileLifecycleManager(handler,
tileServices.getContext(), tileServices,
new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
- new Intent().setComponent(component), userTracker.getUserHandle()));
+ new Intent(TileService.ACTION_QS_TILE).setComponent(component),
+ userTracker.getUserHandle()));
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 87350b46..c130b39 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController;
@@ -133,7 +134,8 @@
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
- KeyguardTransitionInteractor keyguardTransitionInteractor
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
+ PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -160,6 +162,7 @@
KeyguardBouncerViewBinder.bind(
mView.findViewById(R.id.keyguard_bouncer_container),
keyguardBouncerViewModel,
+ primaryBouncerToGoneTransitionViewModel,
keyguardBouncerComponentFactory);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index f0d064b..9a9503c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -572,8 +572,7 @@
entry.setGroupExpansionChanging(true)
userId = entry.sbn.userId
}
- var fullShadeNeedsBouncer = (!lockScreenUserManager.userAllowsPrivateNotificationsInPublic(
- lockScreenUserManager.getCurrentUserId()) ||
+ var fullShadeNeedsBouncer = (
!lockScreenUserManager.shouldShowLockscreenNotifications() ||
falsingCollector.shouldEnforceBouncer())
if (keyguardBypassController.bypassEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index f395bea..82c5ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -334,9 +334,9 @@
}
val ssView = plugin.getView(parent)
+ configPlugin?.let { ssView.registerConfigProvider(it) }
ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
ssView.registerDataProvider(plugin)
- configPlugin?.let { ssView.registerConfigProvider(it) }
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 39e4000..4522e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -443,6 +443,7 @@
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
() -> runningInflations.values().forEach(CancellationSignal::cancel));
+
return cancellationSignal;
}
@@ -783,6 +784,7 @@
public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
implements InflationCallback, InflationTask {
+ private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L;
private final NotificationEntry mEntry;
private final Context mContext;
private final boolean mInflateSynchronously;
@@ -876,7 +878,7 @@
recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState();
- return inflateSmartReplyViews(
+ InflationProgress result = inflateSmartReplyViews(
inflationProgress,
mReInflateFlags,
mEntry,
@@ -884,6 +886,11 @@
packageContext,
previousSmartReplyState,
mSmartRepliesInflater);
+
+ // wait for image resolver to finish preloading
+ mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS);
+
+ return result;
} catch (Exception e) {
mError = e;
return null;
@@ -918,6 +925,9 @@
mCallback.handleInflationException(mRow.getEntry(),
new InflationException("Couldn't inflate contentViews" + e));
}
+
+ // Cancel any image loading tasks, not useful any more
+ mRow.getImageResolver().cancelRunningTasks();
}
@Override
@@ -944,6 +954,9 @@
// Notify the resolver that the inflation task has finished,
// try to purge unnecessary cached entries.
mRow.getImageResolver().purgeCache();
+
+ // Cancel any image loading tasks that have not completed at this point
+ mRow.getImageResolver().cancelRunningTasks();
}
private static class RtlEnabledContext extends ContextWrapper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index 41eeada0..fe0b312 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -22,8 +22,11 @@
import android.util.Log;
import java.util.Set;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* A cache for inline images of image messages.
@@ -56,12 +59,13 @@
}
@Override
- public Drawable get(Uri uri) {
+ public Drawable get(Uri uri, long timeoutMs) {
Drawable result = null;
try {
- result = mCache.get(uri).get();
- } catch (InterruptedException | ExecutionException ex) {
- Log.d(TAG, "get: Failed get image from " + uri);
+ result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException
+ | TimeoutException | CancellationException ex) {
+ Log.d(TAG, "get: Failed get image from " + uri + " " + ex);
}
return result;
}
@@ -72,6 +76,15 @@
mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey()));
}
+ @Override
+ public void cancelRunningTasks() {
+ mCache.forEach((key, value) -> {
+ if (value.getStatus() != AsyncTask.Status.FINISHED) {
+ value.cancel(true);
+ }
+ });
+ }
+
private static class PreloadImageTask extends AsyncTask<Uri, Void, Drawable> {
private final NotificationInlineImageResolver mResolver;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index b05e64ab..c620f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -23,6 +23,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.Log;
import com.android.internal.R;
@@ -45,6 +46,9 @@
public class NotificationInlineImageResolver implements ImageResolver {
private static final String TAG = NotificationInlineImageResolver.class.getSimpleName();
+ // Timeout for loading images from ImageCache when calling from UI thread
+ private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L;
+
private final Context mContext;
private final ImageCache mImageCache;
private Set<Uri> mWantedUriSet;
@@ -123,17 +127,25 @@
return null;
}
+ /**
+ * Loads an image from the Uri.
+ * This method is synchronous and is usually called from the Main thread.
+ * It will time-out after MAX_UI_THREAD_TIMEOUT_MS.
+ *
+ * @param uri Uri of the target image.
+ * @return drawable of the image, null if loading failed/timeout
+ */
@Override
public Drawable loadImage(Uri uri) {
- return hasCache() ? loadImageFromCache(uri) : resolveImage(uri);
+ return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri);
}
- private Drawable loadImageFromCache(Uri uri) {
+ private Drawable loadImageFromCache(Uri uri, long timeoutMs) {
// if the uri isn't currently cached, try caching it first
if (!mImageCache.hasEntry(uri)) {
mImageCache.preload((uri));
}
- return mImageCache.get(uri);
+ return mImageCache.get(uri, timeoutMs);
}
/**
@@ -208,6 +220,30 @@
}
/**
+ * Wait for a maximum timeout for images to finish preloading
+ * @param timeoutMs total timeout time
+ */
+ void waitForPreloadedImages(long timeoutMs) {
+ if (!hasCache()) {
+ return;
+ }
+ Set<Uri> preloadedUris = getWantedUriSet();
+ if (preloadedUris != null) {
+ // Decrement remaining timeout after each image check
+ long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs;
+ preloadedUris.forEach(
+ uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime()));
+ }
+ }
+
+ void cancelRunningTasks() {
+ if (!hasCache()) {
+ return;
+ }
+ mImageCache.cancelRunningTasks();
+ }
+
+ /**
* A interface for internal cache implementation of this resolver.
*/
interface ImageCache {
@@ -216,7 +252,7 @@
* @param uri The uri of the image.
* @return Drawable of the image.
*/
- Drawable get(Uri uri);
+ Drawable get(Uri uri, long timeoutMs);
/**
* Set the image resolver that actually resolves image from specified uri.
@@ -241,6 +277,11 @@
* Purge unnecessary entries in the cache.
*/
void purge();
+
+ /**
+ * Cancel all unfinished image loading tasks
+ */
+ void cancelRunningTasks();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index d6dc671..664d61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3747,6 +3747,12 @@
@Override
public void notifyBiometricAuthModeChanged() {
mDozeServiceHost.updateDozing();
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_DISMISS_BOUNCER) {
+ // Don't update the scrim controller at this time, in favor of the transition repository
+ // updating the scrim
+ return;
+ }
updateScrimController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb8bf52..ce650d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
import static java.lang.Float.isNaN;
import android.animation.Animator;
@@ -53,7 +55,11 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -71,6 +77,8 @@
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
@@ -251,6 +259,28 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
+ private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private CoroutineDispatcher mMainDispatcher;
+ private boolean mIsBouncerToGoneTransitionStarted = false;
+ private boolean mIsBouncerToGoneTransitionRunning = false;
+ private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ private final Consumer<Float> mScrimAlphaConsumer =
+ (Float alpha) -> {
+ mScrimInFront.setViewAlpha(0f);
+ mNotificationsScrim.setViewAlpha(0f);
+ mScrimBehind.setViewAlpha(alpha);
+ };
+ final Consumer<TransitionStep> mPrimaryBouncerToGoneTransition =
+ (TransitionStep step) -> {
+ mIsBouncerToGoneTransitionRunning =
+ step.getTransitionState() == TransitionState.RUNNING;
+ mIsBouncerToGoneTransitionStarted =
+ step.getTransitionState() == TransitionState.STARTED;
+ if (mIsBouncerToGoneTransitionStarted) {
+ transitionTo(ScrimState.UNLOCKED);
+ }
+ };
+
@Inject
public ScrimController(
LightBarController lightBarController,
@@ -265,7 +295,10 @@
@Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
+ @Main CoroutineDispatcher mainDispatcher) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
@@ -304,6 +337,9 @@
}
});
mColors = new GradientColors();
+ mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mMainDispatcher = mainDispatcher;
}
/**
@@ -343,6 +379,11 @@
for (ScrimState state : ScrimState.values()) {
state.prepare(state);
}
+
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
+ mPrimaryBouncerToGoneTransition, mMainDispatcher);
+ collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
+ mScrimAlphaConsumer, mMainDispatcher);
}
// TODO(b/270984686) recompute scrim height accurately, based on shade contents.
@@ -372,6 +413,11 @@
}
public void transitionTo(ScrimState state, Callback callback) {
+ if (mIsBouncerToGoneTransitionRunning) {
+ Log.i(TAG, "Skipping transition to: " + state
+ + " while mIsBouncerToGoneTransitionRunning");
+ return;
+ }
if (state == mState) {
// Call the callback anyway, unless it's already enqueued
if (callback != null && mCallback != callback) {
@@ -797,10 +843,11 @@
mBehindAlpha = 0;
mNotificationsAlpha = 0;
} else {
- // Behind scrim will finish fading in at 30% expansion.
float behindFraction = MathUtils
.constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
- mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+ if (!mIsBouncerToGoneTransitionStarted) {
+ mBehindAlpha = behindFraction * mDefaultScrimAlpha;
+ }
// Delay fade-in of notification scrim a bit further, to coincide with the
// behind scrim finishing fading in.
// Also to coincide with the view starting to fade in, otherwise the empty
@@ -1138,7 +1185,9 @@
Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
Color.alpha(tint));
scrimView.setTint(tint);
- scrimView.setViewAlpha(alpha);
+ if (!mIsBouncerToGoneTransitionRunning) {
+ scrimView.setViewAlpha(alpha);
+ }
} else {
scrim.setAlpha(alpha);
}
@@ -1486,6 +1535,9 @@
}
public void setKeyguardOccluded(boolean keyguardOccluded) {
+ if (mKeyguardOccluded == keyguardOccluded) {
+ return;
+ }
mKeyguardOccluded = keyguardOccluded;
updateScrims();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
new file mode 100644
index 0000000..a2001e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 0469e77..0e6f8d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -219,6 +219,29 @@
}
@Test
+ fun isKeyguardUnlocked() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(true)
+ captor.value.onUnlockedChanged()
+ assertThat(latest).isTrue()
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ captor.value.onUnlockedChanged()
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun isDozing() =
runTest(UnconfinedTestDispatcher()) {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index ae7a928..fe9098f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,6 +19,8 @@
import android.animation.ValueAnimator
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
import com.android.systemui.flags.FakeFeatureFlags
@@ -40,6 +42,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelChildren
@@ -51,6 +54,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -77,6 +82,7 @@
// Used to verify transition requests for test output
@Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -102,6 +108,8 @@
transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository)
+ whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
+
val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
@@ -173,16 +181,17 @@
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardSecurityModel = keyguardSecurityModel,
)
fromPrimaryBouncerTransitionInteractor.start()
}
@Test
- fun `DREAMING to LOCKSCREEN - dreaming state changes first`() =
+ fun `DREAMING to LOCKSCREEN`() =
testScope.runTest {
- // GIVEN a device is dreaming and occluded
+ // GIVEN a device is dreaming
keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setKeyguardOccluded(true)
+ keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent()
// GIVEN a prior transition has run to DREAMING
@@ -215,56 +224,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
- }
- // THEN a transition to BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
- testScope.runTest {
- // GIVEN a device is dreaming and occluded
- keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setKeyguardOccluded(true)
- runCurrent()
-
- // GIVEN a prior transition has run to DREAMING
- runner.startTransition(
- testScope,
- TransitionInfo(
- ownerName = "",
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING,
- animator =
- ValueAnimator().apply {
- duration = 10
- interpolator = Interpolators.LINEAR
- },
- )
- )
- runCurrent()
- reset(mockTransitionRepository)
-
- // WHEN doze is complete
- keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
- )
- // AND occluded has stopped
- keyguardRepository.setKeyguardOccluded(false)
- advanceUntilIdle()
- // AND then dreaming has stopped
- keyguardRepository.setDreamingWithOverlay(false)
- advanceUntilIdle()
-
- val info =
- withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -304,7 +264,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -345,7 +305,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -386,7 +346,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -427,7 +387,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -468,7 +428,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -505,7 +465,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -542,7 +502,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -583,7 +543,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -624,7 +584,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -661,7 +621,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -677,6 +637,7 @@
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
@@ -704,7 +665,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -741,7 +702,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -784,7 +745,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -828,7 +789,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -870,7 +831,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -912,7 +873,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -954,7 +915,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -995,7 +956,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 56e060d..17d8799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -16,12 +16,13 @@
package com.android.systemui.media.dialog;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
-import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_NONE;
import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
import static android.media.RouteListingPreference.Item.SUBTEXT_CUSTOM;
import static android.media.RouteListingPreference.Item.SUBTEXT_SUBSCRIPTION_REQUIRED;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_GO_TO_APP;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 0a401b0..82a5743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
@@ -65,48 +66,32 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var view: NotificationShadeWindowView
- @Mock
- private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
- @Mock
- private lateinit var centralSurfaces: CentralSurfaces
- @Mock
- private lateinit var dockManager: DockManager
- @Mock
- private lateinit var notificationPanelViewController: NotificationPanelViewController
- @Mock
- private lateinit var notificationShadeDepthController: NotificationShadeDepthController
- @Mock
- private lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock
- private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
- @Mock
- private lateinit var ambientState: AmbientState
- @Mock
- private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
- @Mock
- private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
- @Mock
- private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
- @Mock
- private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+ @Mock private lateinit var view: NotificationShadeWindowView
+ @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dockManager: DockManager
+ @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock private lateinit var ambientState: AmbientState
+ @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+ @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
- @Mock
- private lateinit var lockIconViewController: LockIconViewController
- @Mock
- private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
- @Mock
- private lateinit var pulsingGestureListener: PulsingGestureListener
- @Mock
- private lateinit var notificationInsetsController: NotificationInsetsController
- @Mock
- private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var lockIconViewController: LockIconViewController
+ @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
+ @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock private lateinit var notificationInsetsController: NotificationInsetsController
+ @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock
+ lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -118,43 +103,44 @@
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
- .thenReturn(mock(ViewGroup::class.java))
+ .thenReturn(mock(ViewGroup::class.java))
whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
- .thenReturn(keyguardBouncerComponent)
+ .thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
- .thenReturn(keyguardSecurityContainerController)
+ .thenReturn(keyguardSecurityContainerController)
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
- .thenReturn(emptyFlow<TransitionStep>())
- underTest = NotificationShadeWindowViewController(
- lockscreenShadeTransitionController,
- FalsingCollectorFake(),
- sysuiStatusBarStateController,
- dockManager,
- notificationShadeDepthController,
- view,
- notificationPanelViewController,
- ShadeExpansionStateManager(),
- stackScrollLayoutController,
- statusBarKeyguardViewManager,
- statusBarWindowStateController,
- lockIconViewController,
- centralSurfaces,
- notificationShadeWindowController,
- keyguardUnlockAnimationController,
- notificationInsetsController,
- ambientState,
- pulsingGestureListener,
- keyguardBouncerViewModel,
- keyguardBouncerComponentFactory,
- alternateBouncerInteractor,
- keyguardTransitionInteractor,
- )
+ .thenReturn(emptyFlow<TransitionStep>())
+ underTest =
+ NotificationShadeWindowViewController(
+ lockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ sysuiStatusBarStateController,
+ dockManager,
+ notificationShadeDepthController,
+ view,
+ notificationPanelViewController,
+ ShadeExpansionStateManager(),
+ stackScrollLayoutController,
+ statusBarKeyguardViewManager,
+ statusBarWindowStateController,
+ lockIconViewController,
+ centralSurfaces,
+ notificationShadeWindowController,
+ keyguardUnlockAnimationController,
+ notificationInsetsController,
+ ambientState,
+ pulsingGestureListener,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory,
+ alternateBouncerInteractor,
+ keyguardTransitionInteractor,
+ primaryBouncerToGoneTransitionViewModel,
+ )
underTest.setupExpandedStatusBar()
- interactionEventHandlerCaptor =
- ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+ interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
- interactionEventHandler = interactionEventHandlerCaptor.value
+ interactionEventHandler = interactionEventHandlerCaptor.value
}
// Note: So far, these tests only cover interactions with the status bar view controller. More
@@ -184,14 +170,11 @@
@Test
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
- val downEvBelow = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
- )
+ val downEvBelow =
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
- val nextEvent = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
- )
+ val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 5d71979..faa6221 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -46,6 +46,7 @@
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController;
@@ -101,6 +102,7 @@
@Mock private NotificationInsetsController mNotificationInsetsController;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
@@ -150,7 +152,8 @@
mKeyguardBouncerViewModel,
mKeyguardBouncerComponentFactory,
mAlternateBouncerInteractor,
- mKeyguardTransitionInteractor
+ mKeyguardTransitionInteractor,
+ mPrimaryBouncerToGoneTransitionViewModel
);
mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index d99cdd51..ab615f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -211,16 +211,6 @@
}
@Test
- fun testTriggeringBouncerWhenPrivateNotificationsArentAllowed() {
- whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
- false)
- transitionController.goToLockedShade(null)
- verify(statusbarStateController, never()).setState(anyInt())
- verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
- }
-
- @Test
fun testTriggeringBouncerNoNotificationsOnLockscreen() {
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
transitionController.goToLockedShade(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index dc5a047..180d9f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -24,6 +24,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -58,7 +60,12 @@
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -85,8 +92,10 @@
import java.util.HashSet;
import java.util.Map;
+import kotlinx.coroutines.CoroutineDispatcher;
+
@RunWith(AndroidTestingRunner.class)
[email protected]
[email protected](setAsMainLooper = true)
@SmallTest
public class ScrimControllerTest extends SysuiTestCase {
@@ -115,6 +124,10 @@
@Mock private DockManager mDockManager;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock private CoroutineDispatcher mMainDispatcher;
+
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -225,13 +238,20 @@
when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
when(mDockManager.isDocked()).thenReturn(false);
+ when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
+ .thenReturn(emptyFlow());
+ when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()).thenReturn(emptyFlow());
+
mScrimController = new ScrimController(mLightBarController,
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager);
+ mStatusBarKeyguardViewManager,
+ mPrimaryBouncerToGoneTransitionViewModel,
+ mKeyguardTransitionInteractor,
+ mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -861,7 +881,10 @@
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager);
+ mStatusBarKeyguardViewManager,
+ mPrimaryBouncerToGoneTransitionViewModel,
+ mKeyguardTransitionInteractor,
+ mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1629,6 +1652,18 @@
assertScrimAlpha(mScrimBehind, 0);
}
+ @Test
+ public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.mPrimaryBouncerToGoneTransition.accept(
+ new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+ TransitionState.RUNNING, "ScrimControllerTest"));
+
+ // This request should not happen
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+ assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 1a371c7..194ed02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -47,6 +47,9 @@
private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
+ private val _isKeyguardUnlocked = MutableStateFlow(false)
+ override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked
+
private val _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index eac1bd1..16442bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -37,7 +37,7 @@
_transitions.emit(step)
}
- override fun startTransition(info: TransitionInfo): UUID? {
+ override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
return null
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 43b816b..61032dc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -849,6 +849,32 @@
}
}
+
+ /**
+ * Updates the last fill response when a view was entered.
+ */
+ void logViewEntered(int sessionId, @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (!isValidEventLocked("logViewEntered", sessionId)) {
+ return;
+ }
+
+ if (mEventHistory.getEvents() != null) {
+ // Do not log this event more than once
+ for (Event event : mEventHistory.getEvents()) {
+ if (event.getType() == Event.TYPE_VIEW_REQUESTED_AUTOFILL) {
+ Slog.v(TAG, "logViewEntered: already logged TYPE_VIEW_REQUESTED_AUTOFILL");
+ return;
+ }
+ }
+ }
+
+ mEventHistory.addEvent(
+ new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
+ null, null, null, null, null, null, null));
+ }
+ }
+
void logAugmentedAutofillAuthenticationSelected(int sessionId, @Nullable String selectedDataset,
@Nullable Bundle clientState) {
synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 598521f..4a12e38 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -442,6 +442,18 @@
private boolean mPreviouslyFillDialogPotentiallyStarted;
/**
+ * Keeps track of if the user entered view, this is used to
+ * distinguish Fill Request that did not have user interaction
+ * with ones that did.
+ *
+ * This is set to true when entering view - after FillDialog FillRequest
+ * or on plain user tap.
+ */
+ @NonNull
+ @GuardedBy("mLock")
+ private boolean mLogViewEntered;
+
+ /**
* Keeps the fill dialog trigger ids of the last response. This invalidates
* the trigger ids of the previous response.
*/
@@ -1289,6 +1301,7 @@
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
+ mLogViewEntered = false;
}
/**
@@ -1413,6 +1426,14 @@
mService.setLastResponse(id, response);
+ synchronized (mLock) {
+ if (mLogViewEntered) {
+ mLogViewEntered = false;
+ mService.logViewEntered(id, null);
+ }
+ }
+
+
final long disableDuration = response.getDisableDuration();
final boolean autofillDisabled = disableDuration > 0;
if (autofillDisabled) {
@@ -3545,6 +3566,28 @@
return;
}
+ synchronized (mLock) {
+ if (!mLogViewEntered) {
+ // If the current request is for FillDialog (preemptive)
+ // then this is the first time that the view is entered
+ // (mLogViewEntered == false) in this case, setLastResponse()
+ // has already been called, so just log here.
+ // If the current request is not and (mLogViewEntered == false)
+ // then the last session is being tracked (setLastResponse not called)
+ // so this calling logViewEntered will be a nop.
+ // Calling logViewEntered() twice will only log it once
+ // TODO(271181979): this is broken for multiple partitions
+ mService.logViewEntered(this.id, null);
+ }
+
+ // If this is the first time view is entered for inline, the last
+ // session is still being tracked, so logViewEntered() needs
+ // to be delayed until setLastResponse is called.
+ // For fill dialog requests case logViewEntered is already called above
+ // so this will do nothing. Assumption: only one fill dialog per session
+ mLogViewEntered = true;
+ }
+
// Previously, fill request will only start whenever a view is entered.
// With Fill Dialog, request starts prior to view getting entered. So, we can't end
// the event at this moment, otherwise we will be wrongly attributing fill dialog
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 79709a3..1176178 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -2,7 +2,6 @@
set noparent
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 7261709..287d713 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -36,6 +36,7 @@
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -973,6 +974,7 @@
/* scheduler */ null);
}
+ @NonNull
private ArrayList<FullBackupEntry> readFullBackupSchedule() {
boolean changed = false;
ArrayList<FullBackupEntry> schedule = null;
@@ -986,11 +988,11 @@
DataInputStream in = new DataInputStream(bufStream)) {
int version = in.readInt();
if (version != SCHEDULE_FILE_VERSION) {
- Slog.e(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Unknown backup schedule version " + version));
- return null;
+ // The file version doesn't match the expected value.
+ // Since this is within a "try" block, this exception will be treated like
+ // any other exception, and caught below.
+ throw new IllegalArgumentException("Unknown backup schedule version "
+ + version);
}
final int numPackages = in.readInt();
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 22ac22d..b673fb6 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -49,6 +49,7 @@
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.dex.DynamicCodeLogger;
+import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
@@ -1075,6 +1076,11 @@
public abstract void writePermissionSettings(@NonNull @UserIdInt int[] userIds, boolean async);
/**
+ * Read legacy permission definitions for permissions migration to new permission subsystem.
+ */
+ public abstract LegacyPermissionSettings getLegacyPermissions();
+
+ /**
* Returns {@code true} if the caller is the installer of record for the given package.
* Otherwise, {@code false}.
*/
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 2992bf9..3ecf933 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -34,7 +34,6 @@
import android.content.IntentFilter;
import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.Checksum;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IStagedApexObserver;
@@ -117,14 +116,17 @@
@VisibleForTesting
static final String BINARY_HASH_ERROR = "SHA256HashError";
- static final int MEASURE_APEX_AND_MODULES = 1;
- static final int MEASURE_PRELOADS = 2;
- static final int MEASURE_NEW_MBAS = 3;
-
static final long RECORD_MEASUREMENTS_COOLDOWN_MS = 24 * 60 * 60 * 1000;
static final String APEX_PRELOAD_LOCATION_ERROR = "could-not-be-determined";
+ // Copy from the atom. Consistent for both ApexInfoGathered and MobileBundledAppInfoGathered.
+ static final int DIGEST_ALGORITHM_UNKNOWN = 0;
+ static final int DIGEST_ALGORITHM_CHUNKED_SHA256 = 1;
+ static final int DIGEST_ALGORITHM_CHUNKED_SHA512 = 2;
+ static final int DIGEST_ALGORITHM_VERITY_CHUNKED_SHA256 = 3;
+ static final int DIGEST_ALGORITHM_SHA256 = 4;
+
// used for indicating any type of error during MBA measurement
static final int MBA_STATUS_ERROR = 0;
// used for indicating factory condition preloads
@@ -226,9 +228,9 @@
appInfo.mbaStatus = mbaStatus;
// Only digest and split name are different between splits.
- Checksum checksum = measureApk(split.getPath());
- appInfo.digest = checksum.getValue();
- appInfo.digestAlgorithm = checksum.getType();
+ Digest digest = measureApk(split.getPath());
+ appInfo.digest = digest.value;
+ appInfo.digestAlgorithm = digest.algorithm;
results.add(appInfo);
}
@@ -260,10 +262,9 @@
* Perform basic measurement (i.e. content digest) on a given APK.
*
* @param apkPath The APK (or APEX, since it's also an APK) file to be measured.
- * @return a {@link android.content.pm.Checksum} with preferred digest algorithm type and
- * the checksum.
+ * @return a {@link #Digest} with preferred digest algorithm type and the value.
*/
- private @Nullable Checksum measureApk(@NonNull String apkPath) {
+ private @Nullable Digest measureApk(@NonNull String apkPath) {
// compute content digest
Map<Integer, byte[]> contentDigests = computeApkContentDigest(apkPath);
if (contentDigests == null) {
@@ -274,20 +275,20 @@
// And only one of them will be available per package.
if (contentDigests.containsKey(
ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256)) {
- return new Checksum(
- Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+ return new Digest(
+ DIGEST_ALGORITHM_CHUNKED_SHA256,
contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256));
} else if (contentDigests.containsKey(
ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512)) {
- return new Checksum(
- Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
+ return new Digest(
+ DIGEST_ALGORITHM_CHUNKED_SHA512,
contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512));
}
}
// When something went wrong, fall back to simple sha256.
byte[] digest = PackageUtils.computeSha256DigestForLargeFileAsBytes(apkPath,
PackageUtils.createLargeFileBuffer());
- return new Checksum(Checksum.TYPE_WHOLE_SHA256, digest);
+ return new Digest(DIGEST_ALGORITHM_SHA256, digest);
}
@@ -350,7 +351,7 @@
// lastly measure all newly installed MBAs
List<IBinaryTransparencyService.AppInfo> allMbaInfo =
collectAllSilentInstalledMbaInfo(packagesMeasured);
- for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
+ for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
packagesMeasured.putBoolean(appInfo.packageName, true);
writeAppInfoToLog(appInfo);
}
@@ -381,7 +382,7 @@
Slog.w(TAG, "Skipping the missing APK in " + pkg.getPath());
continue;
}
- Checksum apexChecksum = measureApk(pkg.getPath());
+ Digest apexChecksum = measureApk(pkg.getPath());
if (apexChecksum == null) {
Slog.w(TAG, "Skipping the missing APEX in " + pkg.getPath());
continue;
@@ -390,8 +391,8 @@
var apexInfo = new IBinaryTransparencyService.ApexInfo();
apexInfo.packageName = packageState.getPackageName();
apexInfo.longVersion = packageState.getVersionCode();
- apexInfo.digest = apexChecksum.getValue();
- apexInfo.digestAlgorithm = apexChecksum.getType();
+ apexInfo.digest = apexChecksum.value;
+ apexInfo.digestAlgorithm = apexChecksum.algorithm;
apexInfo.signerDigests =
computePackageSignerSha256Digests(packageState.getSigningInfo());
@@ -1691,4 +1692,14 @@
}
return slice.getList();
}
+
+ private static class Digest {
+ public int algorithm;
+ public byte[] value;
+
+ Digest(int algorithm, byte[] value) {
+ this.algorithm = algorithm;
+ this.value = value;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 225afea..a60f06a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4945,10 +4945,6 @@
if (intent.getClipData() == null) {
intent.setClipData(ClipData.newPlainText(null, null));
}
- intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
final long bid = Binder.clearCallingIdentity();
try {
PackageManager pm = mContext.getPackageManager();
@@ -4994,7 +4990,19 @@
if (intent == null) {
return (simulateIntent == null);
}
- return intent.filterEquals(simulateIntent);
+ if (!intent.filterEquals(simulateIntent)) {
+ return false;
+ }
+
+ if (intent.getSelector() != simulateIntent.getSelector()) {
+ return false;
+ }
+
+ int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
+ return (simulateIntent.getFlags() & prohibitedFlags) == 0;
}
private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c1850bd..f90a3ce 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -137,6 +137,7 @@
import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException;
import android.app.Service;
import android.app.ServiceStartArgs;
+import android.app.StartForegroundCalledOnStoppedServiceException;
import android.app.admin.DevicePolicyEventLogger;
import android.app.compat.CompatChanges;
import android.app.usage.UsageEvents;
@@ -800,13 +801,17 @@
? res.permission : "private to package");
}
-
- // TODO(short-service): This is inside startService() / startForegroundService().
- // Consider if there's anything special we have to do if these are called on an already-
- // running short-FGS... But given these APIs shouldn't change the FGS type, we likely
- // don't need to do anything. (If they would change the FGS type, we'd have to stop
- // the timeout)
ServiceRecord r = res.record;
+ // Note, when startService() or startForegroundService() is called on an already
+ // running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
+ // ForegroundServiceStartNotAllowedException), even when the service is alerady timed
+ // out. This is because these APIs will essnetially only change the "started" state
+ // of the service, and it won't afect "the foreground-ness" of the service, or the type
+ // of the FGS.
+ // However, this call will still _not_ extend the SHORT_SERVICE timeout either.
+ // Also, if the app tries to change the type of the FGS later (using
+ // Service.startForeground()), at that point we will consult the BFSL check and the timeout
+ // and make the necessary decisions.
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
backgroundStartPrivileges, false /* isBindService */);
@@ -2035,8 +2040,7 @@
// suddenly disallow it.
// However, this would be very problematic if used with a short-FGS, so we
// explicitly disallow this combination.
- // TODO(short-service): Change to another exception type?
- throw new IllegalStateException(
+ throw new StartForegroundCalledOnStoppedServiceException(
"startForeground(SHORT_SERVICE) called on a service that's not"
+ " started.");
}
@@ -2241,10 +2245,6 @@
cancelForegroundNotificationLocked(r);
r.foregroundId = id;
}
-
- // TODO(short-service): Stop the short service timeout, if the type is changing
- // from short to non-short. (should we do it earlier?)
-
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
r.foregroundServiceType = foregroundServiceType;
@@ -3190,7 +3190,7 @@
try {
sr.app.getThread().scheduleTimeoutService(sr, sr.getShortFgsInfo().getStartId());
} catch (RemoteException e) {
- // TODO(short-service): Anything to do here?
+ Slog.w(TAG_SERVICE, "Exception from scheduleTimeoutService: " + e.toString());
}
// Schedule the procstate demotion timeout and ANR timeout.
{
@@ -3262,8 +3262,9 @@
}
mAm.appNotResponding(sr.app, tr);
- // TODO(short-service): Make sure, if the FGS stops after this, the ANR dialog
- // disappears.
+ // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
+ // dialog really doesn't remember the "cause" (especially if there have been multiple
+ // ANRs), so it's not doable.
}
}
@@ -3278,8 +3279,8 @@
}
}
- // TODO(short-service): Hmm what is it? Should we stop the timeout here?
private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
+ maybeStopShortFgsTimeoutLocked(service);
final ProcessServiceRecord psr = service.app.mServices;
psr.stopService(service);
psr.updateBoundClientUids();
@@ -5406,8 +5407,6 @@
// Check to see if the service had been started as foreground, but being
// brought down before actually showing a notification. That is not allowed.
- // TODO(short-service): This is unlikely related to short-FGS, but I'm curious why it's
- // not allowed. Look into it.
if (r.fgRequired) {
Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
+ r);
@@ -5478,6 +5477,7 @@
cancelForegroundNotificationLocked(r);
final boolean exitingFg = r.isForeground;
if (exitingFg) {
+ maybeStopShortFgsTimeoutLocked(r);
decActiveForegroundAppLocked(smap, r);
synchronized (mAm.mProcessStats.mLock) {
ServiceState stracker = r.getTracker();
@@ -5501,8 +5501,6 @@
mFGSLogger.logForegroundServiceStop(r.appInfo.uid, r);
}
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
-
- // TODO(short-service): Make sure we stop the timeout by here.
}
r.isForeground = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a8054de..b7ca23a4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,7 +32,6 @@
import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -3328,7 +3327,6 @@
}
mBatteryStatsService.noteProcessDied(app.info.uid, pid);
- mOomAdjuster.updateShortFgsOwner(app.info.uid, pid, false);
if (!app.isKilled()) {
if (!fromBinderDied) {
@@ -15578,16 +15576,6 @@
return false;
}
- final ApplicationInfo sdkSandboxInfo;
- try {
- final PackageManager pm = mContext.getPackageManager();
- sdkSandboxInfo = pm.getApplicationInfoAsUser(pm.getSdkSandboxPackageName(), 0, userId);
- } catch (NameNotFoundException e) {
- reportStartInstrumentationFailureLocked(
- watcher, className, "Can't find SdkSandbox package");
- return false;
- }
-
final SdkSandboxManagerLocal sandboxManagerLocal =
LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
if (sandboxManagerLocal == null) {
@@ -15596,12 +15584,20 @@
return false;
}
- final String processName = sandboxManagerLocal.getSdkSandboxProcessNameForInstrumentation(
- sdkSandboxClientAppInfo);
+ final ApplicationInfo sdkSandboxInfo;
+ try {
+ sdkSandboxInfo =
+ sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation(
+ sdkSandboxClientAppInfo, userId);
+ } catch (NameNotFoundException e) {
+ reportStartInstrumentationFailureLocked(
+ watcher, className, "Can't find SdkSandbox package");
+ return false;
+ }
ActiveInstrumentation activeInstr = new ActiveInstrumentation(this);
activeInstr.mClass = className;
- activeInstr.mTargetProcesses = new String[]{processName};
+ activeInstr.mTargetProcesses = new String[]{sdkSandboxInfo.processName};
activeInstr.mTargetInfo = sdkSandboxInfo;
activeInstr.mProfileFile = profileFile;
activeInstr.mArguments = arguments;
@@ -15635,7 +15631,7 @@
ProcessRecord app = addAppLocked(
sdkSandboxInfo,
- processName,
+ sdkSandboxInfo.processName,
/* isolated= */ false,
/* isSdkSandbox= */ true,
sdkSandboxUid,
@@ -18719,23 +18715,6 @@
}
@Override
- public boolean canHoldWakeLocksInDeepDoze(int uid, int procstate) {
- // This method is called with the PowerManager lock held. Do not hold AM here.
-
- // If the procstate is high enough, it's always allowed.
- if (procstate <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
- return true;
- }
- // IF it's too low, it's not allowed.
- if (procstate > PROCESS_STATE_IMPORTANT_FOREGROUND) {
- return false;
- }
- // If it's PROCESS_STATE_IMPORTANT_FOREGROUND, then we allow it only wheen the UID
- // has a SHORT_FGS.
- return mOomAdjuster.hasUidShortForegroundService(uid);
- }
-
- @Override
public boolean startProfileEvenWhenDisabled(@UserIdInt int userId) {
return mUserController.startProfile(userId, /* evenWhenDisabled= */ true,
/* unlockListener= */ null);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4c1835e..34002a9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1063,16 +1063,26 @@
}
@NeverCompile
- int runCompact(PrintWriter pw) {
+ int runCompact(PrintWriter pw) throws RemoteException {
ProcessRecord app;
String op = getNextArgRequired();
boolean isFullCompact = op.equals("full");
boolean isSomeCompact = op.equals("some");
if (isFullCompact || isSomeCompact) {
String processName = getNextArgRequired();
- String uid = getNextArgRequired();
synchronized (mInternal.mProcLock) {
- app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
+ // Default to current user
+ int userId = mInterface.getCurrentUserId();
+ String userOpt = getNextOption();
+ if (userOpt != null && "--user".equals(userOpt)) {
+ int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
+ if (inputUserId != UserHandle.USER_CURRENT) {
+ userId = inputUserId;
+ }
+ }
+ final int uid =
+ mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
+ app = mInternal.getProcessRecordLocked(processName, uid);
}
pw.println("Process record found pid: " + app.mPid);
if (isFullCompact) {
@@ -1098,6 +1108,28 @@
mInternal.mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
}
pw.println("Finished system compaction");
+ } else if (op.equals("native")) {
+ op = getNextArgRequired();
+ isFullCompact = op.equals("full");
+ isSomeCompact = op.equals("some");
+ int pid;
+ String pidStr = getNextArgRequired();
+ try {
+ pid = Integer.parseInt(pidStr);
+ } catch (Exception e) {
+ getErrPrintWriter().println("Error: failed to parse '" + pidStr + "' as a PID");
+ return -1;
+ }
+ if (isFullCompact) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactNative(
+ CachedAppOptimizer.CompactProfile.FULL, pid);
+ } else if (isSomeCompact) {
+ mInternal.mOomAdjuster.mCachedAppOptimizer.compactNative(
+ CachedAppOptimizer.CompactProfile.SOME, pid);
+ } else {
+ getErrPrintWriter().println("Error: unknown compaction type '" + op + "'");
+ return -1;
+ }
} else {
getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
return -1;
@@ -4007,11 +4039,17 @@
pw.println(" --allow-background-activity-starts: The receiver may start activities");
pw.println(" even if in the background.");
pw.println(" --async: Send without waiting for the completion of the receiver.");
- pw.println(" compact [some|full|system] <process_name> <Package UID>");
- pw.println(" Force process compaction.");
+ pw.println(" compact [some|full] <process_name> [--user <USER_ID>]");
+ pw.println(" Perform a single process compaction.");
pw.println(" some: execute file compaction.");
pw.println(" full: execute anon + file compaction.");
pw.println(" system: system compaction.");
+ pw.println(" compact system");
+ pw.println(" Perform a full system compaction.");
+ pw.println(" compact native [some|full] <pid>");
+ pw.println(" Perform a native compaction for process with <pid>.");
+ pw.println(" some: execute file compaction.");
+ pw.println(" full: execute anon + file compaction.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 841b61e..3b386dd 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1444,13 +1444,15 @@
@Override
public boolean isDelayBehindServices() {
- // TODO: implement
+ // Modern queue does not alter the broadcasts delivery behavior based on background
+ // services, so ignore.
return false;
}
@Override
public void backgroundServicesFinishedLocked(int userId) {
- // TODO: implement
+ // Modern queue does not alter the broadcasts delivery behavior based on background
+ // services, so ignore.
}
/**
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f4685f0..8675bfd 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -66,8 +66,6 @@
// Flags stored in the DeviceConfig API.
@VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction";
@VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer";
- @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1";
- @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
@@ -99,15 +97,6 @@
private static final int RSS_ANON_INDEX = 2;
private static final int RSS_SWAP_INDEX = 3;
- // Phenotype sends int configurations and we map them to the strings we'll use on device,
- // preventing a weird string value entering the kernel.
- private static final int COMPACT_ACTION_NONE = 0;
- private static final int COMPACT_ACTION_FILE = 1;
- private static final int COMPACT_ACTION_ANON = 2;
- private static final int COMPACT_ACTION_ALL = 3;
-
- private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
-
// Keeps these flags in sync with services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
private static final int COMPACT_ACTION_FILE_FLAG = 1;
private static final int COMPACT_ACTION_ANON_FLAG = 2;
@@ -117,11 +106,11 @@
private static final int FREEZE_BINDER_TIMEOUT_MS = 100;
+ @VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
+
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_ALL;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -156,22 +145,15 @@
@VisibleForTesting
interface ProcessDependencies {
long[] getRss(int pid);
- void performCompaction(CompactAction action, int pid) throws IOException;
+ void performCompaction(CompactProfile action, int pid) throws IOException;
}
// This indicates the compaction we want to perform
public enum CompactProfile {
- SOME, // File compaction
- FULL // File+anon compaction
- }
-
- // Low level actions that can be performed for compaction
- // currently determined by the compaction profile
- public enum CompactAction {
NONE, // No compaction
- FILE, // File+anon compaction
- ANON,
- ALL
+ SOME, // File compaction
+ ANON, // Anon compaction
+ FULL // File+anon compaction
}
// This indicates the process OOM memory state that initiated the compaction request
@@ -187,6 +169,7 @@
static final int COMPACT_SYSTEM_MSG = 2;
static final int SET_FROZEN_PROCESS_MSG = 3;
static final int REPORT_UNFREEZE_MSG = 4;
+ static final int COMPACT_NATIVE_MSG = 5;
// When free swap falls below this percentage threshold any full (file + anon)
// compactions will be downgraded to file only compactions to reduce pressure
@@ -240,9 +223,6 @@
for (String name : properties.getKeyset()) {
if (KEY_USE_COMPACTION.equals(name)) {
updateUseCompaction();
- } else if (KEY_COMPACT_ACTION_1.equals(name)
- || KEY_COMPACT_ACTION_2.equals(name)) {
- updateCompactionActions();
} else if (KEY_COMPACT_THROTTLE_1.equals(name)
|| KEY_COMPACT_THROTTLE_2.equals(name)
|| KEY_COMPACT_THROTTLE_3.equals(name)
@@ -314,12 +294,6 @@
// Configured by phenotype. Updates from the server take effect immediately.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting
- volatile CompactAction mCompactActionSome = compactActionIntToAction(DEFAULT_COMPACT_ACTION_1);
- @GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting
- volatile CompactAction mCompactActionFull = compactActionIntToAction(DEFAULT_COMPACT_ACTION_2);
- @GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
@@ -542,7 +516,6 @@
CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
synchronized (mPhenotypeFlagLock) {
updateUseCompaction();
- updateCompactionActions();
updateCompactionThrottles();
updateCompactStatsdSampleRate();
updateFreezerStatsdSampleRate();
@@ -587,8 +560,6 @@
pw.println("CachedAppOptimizer settings");
synchronized (mPhenotypeFlagLock) {
pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction);
- pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome);
- pw.println(" " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull);
pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome);
pw.println(" " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull);
pw.println(" " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome);
@@ -761,19 +732,9 @@
return false;
}
- private CompactAction resolveCompactActionForProfile(CompactProfile profile) {
- CompactAction action;
- switch (profile) {
- case SOME:
- action = CompactAction.FILE;
- break;
- case FULL:
- action = CompactAction.ALL;
- break;
- default:
- action = CompactAction.NONE;
- }
- return action;
+ void compactNative(CompactProfile compactProfile, int pid) {
+ mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage(
+ COMPACT_NATIVE_MSG, pid, compactProfile.ordinal()));
}
private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat(
@@ -1051,18 +1012,6 @@
}
@GuardedBy("mPhenotypeFlagLock")
- private void updateCompactionActions() {
- int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1);
-
- int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2);
-
- mCompactActionSome = compactActionIntToAction(compactAction1);
- mCompactActionFull = compactActionIntToAction(compactAction2);
- }
-
- @GuardedBy("mPhenotypeFlagLock")
private void updateCompactionThrottles() {
boolean useThrottleDefaults = false;
// TODO: improve efficiency by calling DeviceConfig only once for all flags.
@@ -1235,14 +1184,6 @@
return true;
}
- static CompactAction compactActionIntToAction(int action) {
- if (action < 0 || action >= CompactAction.values().length) {
- return CompactAction.NONE;
- }
-
- return CompactAction.values()[action];
- }
-
// This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
@GuardedBy("mAm")
void unfreezeTemporarily(ProcessRecord app, @OomAdjuster.OomAdjReason int reason) {
@@ -1475,8 +1416,10 @@
if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
&& (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
- // Perform a minor compaction when a perceptible app becomes the prev/home app
- compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
+ if (ENABLE_FILE_COMPACT) {
+ // Perform a minor compaction when a perceptible app becomes the prev/home app
+ compactApp(app, CompactProfile.SOME, CompactSource.APP, false);
+ }
} else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ
&& newAdj >= ProcessList.CACHED_APP_MIN_ADJ
&& newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
@@ -1486,23 +1429,37 @@
}
/**
- * Applies a compaction downgrade when swap is low.
+ * Computes the final compaction profile to be used which depends on compaction
+ * features enabled and swap usage.
*/
- CompactProfile downgradeCompactionIfRequired(CompactProfile profile) {
- // Downgrade compaction under swap memory pressure
+ CompactProfile resolveCompactionProfile(CompactProfile profile) {
if (profile == CompactProfile.FULL) {
double swapFreePercent = getFreeSwapPercent();
+ // Downgrade compaction under swap memory pressure
if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) {
profile = CompactProfile.SOME;
+
++mTotalCompactionDowngrades;
if (DEBUG_COMPACTION) {
Slog.d(TAG_AM,
- "Downgraded compaction to file only due to low swap."
+ "Downgraded compaction to "+ profile +" due to low swap."
+ " Swap Free% " + swapFreePercent);
}
}
}
+ if (!ENABLE_FILE_COMPACT) {
+ if (profile == CompactProfile.SOME) {
+ profile = CompactProfile.NONE;
+ } else if (profile == CompactProfile.FULL) {
+ profile = CompactProfile.ANON;
+ }
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM,
+ "Final compaction profile "+ profile +" due to file compact disabled");
+ }
+ }
+
return profile;
}
@@ -1733,7 +1690,6 @@
ProcessRecord proc;
final ProcessCachedOptimizerRecord opt;
int pid;
- CompactAction resolvedAction;
final String name;
CompactProfile lastCompactProfile;
long lastCompactTime;
@@ -1811,17 +1767,24 @@
}
CompactProfile resolvedProfile =
- downgradeCompactionIfRequired(requestedProfile);
- resolvedAction = resolveCompactActionForProfile(resolvedProfile);
+ resolveCompactionProfile(requestedProfile);
+ if (resolvedProfile == CompactProfile.NONE) {
+ // No point on issuing compaction call as we don't want to compact.
+ if (DEBUG_COMPACTION) {
+ Slog.d(TAG_AM, "Resolved no compaction for "+ name +
+ " requested profile="+requestedProfile);
+ }
+ return;
+ }
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- "Compact " + resolvedAction.name() + ": " + name
+ "Compact " + resolvedProfile.name() + ": " + name
+ " lastOomAdjReason: " + oomAdjReason
+ " source: " + compactSource.name());
long zramUsedKbBefore = getUsedZramMemory();
long startCpuTime = threadCpuTimeNs();
- mProcessDependencies.performCompaction(resolvedAction, pid);
+ mProcessDependencies.performCompaction(resolvedProfile, pid);
long endCpuTime = threadCpuTimeNs();
long[] rssAfter = mProcessDependencies.getRss(pid);
long end = SystemClock.uptimeMillis();
@@ -1877,7 +1840,7 @@
return;
}
EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name,
- resolvedAction.name(), rssBefore[RSS_TOTAL_INDEX],
+ resolvedProfile.name(), rssBefore[RSS_TOTAL_INDEX],
rssBefore[RSS_FILE_INDEX], rssBefore[RSS_ANON_INDEX],
rssBefore[RSS_SWAP_INDEX], deltaTotalRss, deltaFileRss,
deltaAnonRss, deltaSwapRss, time, lastCompactProfile.name(),
@@ -1907,6 +1870,21 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
+ case COMPACT_NATIVE_MSG: {
+ int pid = msg.arg1;
+ CompactProfile compactProfile = CompactProfile.values()[msg.arg2];
+ Slog.d(TAG_AM,
+ "Performing native compaction for pid=" + pid
+ + " type=" + compactProfile.name());
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem");
+ try {
+ mProcessDependencies.performCompaction(compactProfile, pid);
+ } catch (Exception e) {
+ Slog.d(TAG_AM, "Failed compacting native pid= " + pid);
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ break;
+ }
}
}
}
@@ -2170,14 +2148,14 @@
// Compact process.
@Override
- public void performCompaction(CompactAction action, int pid) throws IOException {
+ public void performCompaction(CompactProfile profile, int pid) throws IOException {
mPidCompacting = pid;
- if (action == CompactAction.ALL) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
- } else if (action == CompactAction.FILE) {
- compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
- } else if (action == CompactAction.ANON) {
- compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
+ if (profile == CompactProfile.FULL) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+ } else if (profile == CompactProfile.SOME) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+ } else if (profile == CompactProfile.ANON) {
+ compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
}
mPidCompacting = -1;
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 48df1494..bb5f31a 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -250,7 +250,6 @@
if (r != null && cpr.canRunHere(r)) {
checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
cpr.name.flattenToShortString(), startTime);
- enforceContentProviderRestrictionsForSdkSandbox(cpi);
// This provider has been published or is in the process
// of being published... but it is also allowed to run
@@ -445,7 +444,6 @@
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
- enforceContentProviderRestrictionsForSdkSandbox(cpi);
return cpr.newHolder(null, true);
}
@@ -594,8 +592,6 @@
// Return a holder instance even if we are waiting for the publishing of the
// provider, client will check for the holder.provider to see if it needs to wait
// for it.
- //todo(b/265965249) Need to perform cleanup before calling enforce method here
- enforceContentProviderRestrictionsForSdkSandbox(cpi);
return cpr.newHolder(conn, false);
}
}
@@ -657,7 +653,6 @@
+ " caller=" + callerName + "/" + Binder.getCallingUid());
return null;
}
- enforceContentProviderRestrictionsForSdkSandbox(cpi);
return cpr.newHolder(conn, false);
}
@@ -1136,7 +1131,6 @@
appName = r.toString();
}
- enforceContentProviderRestrictionsForSdkSandbox(cpi);
return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(),
userId, checkUser, appName);
}
@@ -1511,11 +1505,17 @@
/**
* Check if {@link ProcessRecord} has a possible chance at accessing the
- * given {@link ProviderInfo}. Final permission checking is always done
+ * given {@link ProviderInfo}. First permission checking is for enforcing
+ * ContentProvider Restrictions from SdkSandboxManager.
+ * Final permission checking is always done
* in {@link ContentProvider}.
*/
private String checkContentProviderPermission(ProviderInfo cpi, int callingPid, int callingUid,
int userId, boolean checkUser, String appName) {
+ if (!canAccessContentProviderFromSdkSandbox(cpi, callingUid)) {
+ return "ContentProvider access not allowed from sdk sandbox UID. "
+ + "ProviderInfo: " + cpi.toString();
+ }
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -1905,11 +1905,10 @@
}
}
- // Binder.clearCallingIdentity() shouldn't be called before this method
- // as Binder should have its original callingUid for the check
- private void enforceContentProviderRestrictionsForSdkSandbox(ProviderInfo cpi) {
- if (!Process.isSdkSandboxUid(Binder.getCallingUid())) {
- return;
+ private boolean canAccessContentProviderFromSdkSandbox(ProviderInfo cpi,
+ int callingUid) {
+ if (!Process.isSdkSandboxUid(callingUid)) {
+ return true;
}
final SdkSandboxManagerLocal sdkSandboxManagerLocal =
LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
@@ -1918,11 +1917,7 @@
+ "when checking whether SDK sandbox uid may "
+ "access the contentprovider.");
}
- if (!sdkSandboxManagerLocal
- .canAccessContentProviderFromSdkSandbox(cpi)) {
- throw new SecurityException(
- "SDK sandbox uid may not access contentprovider " + cpi.name);
- }
+ return sdkSandboxManagerLocal.canAccessContentProviderFromSdkSandbox(cpi);
}
/**
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 3792625..9ff2cd0 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -31,11 +31,17 @@
// process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
// elapsed, after which the current count for this breakdown will be reset.
private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS;
- // The time duration after which the rate limit buffer will be cleared.
- private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION;
+ // Indicated how many buffer durations to wait before the rate limit buffer will be cleared.
+ // E.g. if set to 3 will wait 3xRATE_LIMIT_BUFFER_DURATION before clearing the buffer.
+ private static final long RATE_LIMIT_BUFFER_EXPIRY_FACTOR = 3;
// The number of entries to keep per breakdown of process/eventType.
private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6;
+ // If a process is rate limited twice in a row we consider it crash-looping and rate limit it
+ // more aggressively.
+ private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
+ private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+
@GuardedBy("mErrorClusterRecords")
private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
private final Clock mClock;
@@ -71,15 +77,27 @@
return new RateLimitResult(false, 0);
}
- if (now - errRecord.getStartTime() > RATE_LIMIT_BUFFER_DURATION) {
+ final long timeSinceFirstError = now - errRecord.getStartTime();
+ if (timeSinceFirstError > errRecord.getBufferDuration()) {
final int errCount = recentlyDroppedCount(errRecord);
errRecord.setStartTime(now);
errRecord.setCount(1);
+
+ // If this error happened exactly the next "rate limiting cycle" after the last
+ // error and the previous cycle was rate limiting then increment the successive
+ // rate limiting cycle counter. If a full "cycle" has passed since the last error
+ // then this is no longer a continuous occurrence and will be rate limited normally.
+ if (errCount > 0 && timeSinceFirstError < 2 * errRecord.getBufferDuration()) {
+ errRecord.incrementSuccessiveRateLimitCycles();
+ } else {
+ errRecord.setSuccessiveRateLimitCycles(0);
+ }
+
return new RateLimitResult(false, errCount);
}
errRecord.incrementCount();
- if (errRecord.getCount() > RATE_LIMIT_ALLOWED_ENTRIES) {
+ if (errRecord.getCount() > errRecord.getAllowedEntries()) {
return new RateLimitResult(true, recentlyDroppedCount(errRecord));
}
}
@@ -91,16 +109,19 @@
* dropped. Resets every RATE_LIMIT_BUFFER_DURATION if events are still actively created or
* RATE_LIMIT_BUFFER_EXPIRY if not. */
private int recentlyDroppedCount(ErrorRecord errRecord) {
- if (errRecord == null || errRecord.getCount() < RATE_LIMIT_ALLOWED_ENTRIES) return 0;
- return errRecord.getCount() - RATE_LIMIT_ALLOWED_ENTRIES;
+ if (errRecord == null || errRecord.getCount() < errRecord.getAllowedEntries()) return 0;
+ return errRecord.getCount() - errRecord.getAllowedEntries();
}
- private void maybeRemoveExpiredRecords(long now) {
- if (now - mLastMapCleanUp <= RATE_LIMIT_BUFFER_EXPIRY) return;
+ private void maybeRemoveExpiredRecords(long currentTime) {
+ if (currentTime - mLastMapCleanUp
+ <= RATE_LIMIT_BUFFER_EXPIRY_FACTOR * RATE_LIMIT_BUFFER_DURATION) {
+ return;
+ }
for (int i = mErrorClusterRecords.size() - 1; i >= 0; i--) {
- if (now - mErrorClusterRecords.valueAt(i).getStartTime() > RATE_LIMIT_BUFFER_EXPIRY) {
+ if (mErrorClusterRecords.valueAt(i).hasExpired(currentTime)) {
Counter.logIncrement(
"stability_errors.value_dropbox_buffer_expired_count",
mErrorClusterRecords.valueAt(i).getCount());
@@ -108,7 +129,7 @@
}
}
- mLastMapCleanUp = now;
+ mLastMapCleanUp = currentTime;
}
/** Resets the rate limiter memory. */
@@ -153,10 +174,12 @@
private class ErrorRecord {
long mStartTime;
int mCount;
+ int mSuccessiveRateLimitCycles;
ErrorRecord(long startTime, int count) {
mStartTime = startTime;
mCount = count;
+ mSuccessiveRateLimitCycles = 0;
}
public void setStartTime(long startTime) {
@@ -171,6 +194,14 @@
mCount++;
}
+ public void setSuccessiveRateLimitCycles(int successiveRateLimitCycles) {
+ mSuccessiveRateLimitCycles = successiveRateLimitCycles;
+ }
+
+ public void incrementSuccessiveRateLimitCycles() {
+ mSuccessiveRateLimitCycles++;
+ }
+
public long getStartTime() {
return mStartTime;
}
@@ -178,6 +209,27 @@
public int getCount() {
return mCount;
}
+
+ public int getSuccessiveRateLimitCycles() {
+ return mSuccessiveRateLimitCycles;
+ }
+
+ public boolean isRepeated() {
+ return mSuccessiveRateLimitCycles >= 2;
+ }
+
+ public int getAllowedEntries() {
+ return isRepeated() ? STRICT_RATE_LIMIT_ALLOWED_ENTRIES : RATE_LIMIT_ALLOWED_ENTRIES;
+ }
+
+ public long getBufferDuration() {
+ return isRepeated() ? STRICT_RATE_LIMIT_BUFFER_DURATION : RATE_LIMIT_BUFFER_DURATION;
+ }
+
+ public boolean hasExpired(long currentTime) {
+ long bufferExpiry = RATE_LIMIT_BUFFER_EXPIRY_FACTOR * getBufferDuration();
+ return currentTime - mStartTime > bufferExpiry;
+ }
}
private static class DefaultClock implements Clock {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d05301a..679cf0a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -127,7 +127,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseSetArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.CompositeRWLock;
@@ -367,19 +366,6 @@
@GuardedBy("mService")
private boolean mPendingFullOomAdjUpdate = false;
- /**
- * PIDs that has a SHORT_SERVICE. We need to access it with the PowerManager lock held,
- * so we use a fine-grained lock here.
- */
- @GuardedBy("mPidsWithShortFgs")
- private final ArraySet<Integer> mPidsWithShortFgs = new ArraySet<>();
-
- /**
- * UIDs -> PIDs map, used with mPidsWithShortFgs.
- */
- @GuardedBy("mPidsWithShortFgs")
- private final SparseSetArray<Integer> mUidsToPidsWithShortFgs = new SparseSetArray<>();
-
/** Overrideable by a test */
@VisibleForTesting
protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1956,7 +1942,6 @@
}
}
}
- updateShortFgsOwner(psr.mApp.uid, psr.mApp.mPid, hasShortForegroundServices);
// If the app was recently in the foreground and moved to a foreground service status,
// allow it to get a higher rank in memory for some time, compared to other foreground
@@ -2185,8 +2170,6 @@
}
}
- // TODO(short-service): While-in-user permissions. Do we need any change here for
- // short-FGS? (Likely not)
if (s.isForeground) {
final int fgsType = s.foregroundServiceType;
if (s.mAllowWhileInUsePermissionInFgs) {
@@ -3468,40 +3451,4 @@
mCachedAppOptimizer.unfreezeAppLSP(app, oomAdjReason);
}
}
-
- /**
- * Update {@link #mPidsWithShortFgs} and {@link #mUidsToPidsWithShortFgs} to keep track
- * of which UID/PID has a short FGS.
- *
- * TODO(short-FGS): Remove it and all the relevant code once SHORT_FGS use the FGS procstate.
- */
- void updateShortFgsOwner(int uid, int pid, boolean add) {
- synchronized (mPidsWithShortFgs) {
- if (add) {
- mUidsToPidsWithShortFgs.add(uid, pid);
- mPidsWithShortFgs.add(pid);
- } else {
- mUidsToPidsWithShortFgs.remove(uid, pid);
- mPidsWithShortFgs.remove(pid);
- }
- }
- }
-
- /**
- * Whether a UID has a (non-timed-out) short FGS or not.
- * It's indirectly called by PowerManager, so we can't hold the AM lock in it.
- */
- boolean hasUidShortForegroundService(int uid) {
- synchronized (mPidsWithShortFgs) {
- final ArraySet<Integer> pids = mUidsToPidsWithShortFgs.get(uid);
- if (pids == null || pids.size() == 0) {
- return false;
- }
- for (int i = pids.size() - 1; i >= 0; i--) {
- final int pid = pids.valueAt(i);
- return mPidsWithShortFgs.contains(pid);
- }
- }
- return false;
- }
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f4e2b0f..35f71f7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3100,9 +3100,10 @@
hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName());
final ProcessStateRecord state = r.mState;
- if (!mService.mBooted && !mService.mBooting
+ if (!isolated && !isSdkSandbox
&& userId == UserHandle.USER_SYSTEM
- && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+ && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK
+ && (TextUtils.equals(proc, info.processName))) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
state.setSetSchedGroup(ProcessList.SCHED_GROUP_DEFAULT);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0d0e576..1f24eb3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3928,8 +3928,10 @@
}
@Override
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
/** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
* Part of service interface, check permissions and parameters here
* Note calling package is for logging purposes only, not to be trusted
@@ -4945,8 +4947,10 @@
}
@Override
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
/**
* @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
*/
@@ -7214,8 +7218,10 @@
* @param device the audio device to be affected
* @param deviceVolumeBehavior one of the device behaviors
*/
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
// verify permissions
@@ -7295,8 +7301,11 @@
* @param device the audio output device type
* @return the volume behavior for the device
*/
- @android.annotation.EnforcePermission(anyOf =
- {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+ @android.annotation.EnforcePermission(anyOf = {
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.QUERY_AUDIO_STATE,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify permissions
diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java
index ca97a98..ce68edbb 100644
--- a/services/core/java/com/android/server/cpu/CpuInfoReader.java
+++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java
@@ -21,8 +21,10 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
+import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.LongSparseLongArray;
import android.util.SparseArray;
@@ -50,6 +52,9 @@
private static final String POLICY_DIR_PREFIX = "policy";
private static final String RELATED_CPUS_FILE = "related_cpus";
private static final String AFFECTED_CPUS_FILE = "affected_cpus";
+ // TODO(b/263154344): Avoid reading from cpuinfo_cur_freq because non-root users don't have
+ // read permission for this file. The file permissions are set by the Kernel. Instead, read
+ // the current frequency only from scaling_cur_freq.
private static final String CUR_CPUFREQ_FILE = "cpuinfo_cur_freq";
private static final String MAX_CPUFREQ_FILE = "cpuinfo_max_freq";
private static final String CUR_SCALING_FREQ_FILE = "scaling_cur_freq";
@@ -70,16 +75,18 @@
private static final Pattern TIME_IN_STATE_PATTERN =
Pattern.compile("(?<freqKHz>[0-9]+)\\s(?<time>[0-9]+)");
private static final long MILLIS_PER_CLOCK_TICK = 1000L / Os.sysconf(OsConstants._SC_CLK_TCK);
+ private static final long MIN_READ_INTERVAL_MILLISECONDS = 500;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FLAG_CPUSET_CATEGORY_"}, flag = true, value = {
FLAG_CPUSET_CATEGORY_TOP_APP,
FLAG_CPUSET_CATEGORY_BACKGROUND
})
- private @interface CpusetCategory{}
+ /** package **/ @interface CpusetCategory{}
// TODO(b/242722241): Protect updatable variables with a local lock.
private final File mCpusetDir;
+ private final long mMinReadIntervalMillis;
private final SparseIntArray mCpusetCategoriesByCpus = new SparseIntArray();
private final SparseArray<File> mCpuFreqPolicyDirsById = new SparseArray<>();
private final SparseArray<StaticPolicyInfo> mStaticPolicyInfoById = new SparseArray<>();
@@ -90,16 +97,20 @@
private SparseArray<CpuUsageStats> mCumulativeCpuUsageStats = new SparseArray<>();
private boolean mIsEnabled;
private boolean mHasTimeInStateFile;
+ private long mLastReadUptimeMillis;
+ private SparseArray<CpuInfo> mLastReadCpuInfos;
public CpuInfoReader() {
- this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH));
+ this(new File(CPUSET_DIR_PATH), new File(CPUFREQ_DIR_PATH), new File(PROC_STAT_FILE_PATH),
+ MIN_READ_INTERVAL_MILLISECONDS);
}
@VisibleForTesting
- CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile) {
+ CpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile, long minReadIntervalMillis) {
mCpusetDir = cpusetDir;
mCpuFreqDir = cpuFreqDir;
mProcStatFile = procStatFile;
+ mMinReadIntervalMillis = minReadIntervalMillis;
}
/**
@@ -167,6 +178,16 @@
if (!mIsEnabled) {
return null;
}
+ long uptimeMillis = SystemClock.uptimeMillis();
+ if (mLastReadUptimeMillis > 0
+ && uptimeMillis - mLastReadUptimeMillis < mMinReadIntervalMillis) {
+ Slogf.w(TAG, "Skipping reading from device and returning the last read CpuInfos. "
+ + "Last read was %d ms ago, min read interval is %d ms",
+ uptimeMillis - mLastReadUptimeMillis, mMinReadIntervalMillis);
+ return mLastReadCpuInfos;
+ }
+ mLastReadUptimeMillis = uptimeMillis;
+ mLastReadCpuInfos = null;
SparseArray<CpuUsageStats> cpuUsageStatsByCpus = readLatestCpuUsageStats();
if (cpuUsageStatsByCpus == null || cpuUsageStatsByCpus.size() == 0) {
Slogf.e(TAG, "Failed to read latest CPU usage stats");
@@ -202,6 +223,12 @@
+ " policy ID %d", policyId);
continue;
}
+ if (curFreqKHz > maxFreqKHz) {
+ Slogf.w(TAG, "Current CPU frequency (%d) is greater than maximum CPU frequency"
+ + " (%d) for policy ID (%d). Skipping CPU frequency policy", curFreqKHz,
+ maxFreqKHz, policyId);
+ continue;
+ }
for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) {
int relatedCpuCore = staticPolicyInfo.relatedCpuCores.get(coreIdx);
CpuInfo prevCpuInfo = cpuInfoByCpus.get(relatedCpuCore);
@@ -241,9 +268,73 @@
}
}
}
+ mLastReadCpuInfos = cpuInfoByCpus;
return cpuInfoByCpus;
}
+ /** Dumps the current state. */
+ public void dump(IndentingPrintWriter writer) {
+ writer.printf("*%s*\n", getClass().getSimpleName());
+ writer.increaseIndent(); // Add intend for the outermost block.
+
+ writer.printf("mCpusetDir = %s\n", mCpusetDir.getAbsolutePath());
+ writer.printf("mCpuFreqDir = %s\n", mCpuFreqDir.getAbsolutePath());
+ writer.printf("mProcStatFile = %s\n", mProcStatFile.getAbsolutePath());
+ writer.printf("mIsEnabled = %s\n", mIsEnabled);
+ writer.printf("mHasTimeInStateFile = %s\n", mHasTimeInStateFile);
+ writer.printf("mLastReadUptimeMillis = %d\n", mLastReadUptimeMillis);
+ writer.printf("mMinReadIntervalMillis = %d\n", mMinReadIntervalMillis);
+
+ writer.printf("Cpuset categories by CPU core:\n");
+ writer.increaseIndent();
+ for (int i = 0; i < mCpusetCategoriesByCpus.size(); i++) {
+ writer.printf("CPU core id = %d, %s\n", mCpusetCategoriesByCpus.keyAt(i),
+ toCpusetCategoriesStr(mCpusetCategoriesByCpus.valueAt(i)));
+ }
+ writer.decreaseIndent();
+
+ writer.println("Cpu frequency policy directories by policy id:");
+ writer.increaseIndent();
+ for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) {
+ writer.printf("Policy id = %d, Dir = %s\n", mCpuFreqPolicyDirsById.keyAt(i),
+ mCpuFreqPolicyDirsById.valueAt(i));
+ }
+ writer.decreaseIndent();
+
+ writer.println("Static cpu frequency policy infos by policy id:");
+ writer.increaseIndent();
+ for (int i = 0; i < mStaticPolicyInfoById.size(); i++) {
+ writer.printf("Policy id = %d, %s\n", mStaticPolicyInfoById.keyAt(i),
+ mStaticPolicyInfoById.valueAt(i));
+ }
+ writer.decreaseIndent();
+
+ writer.println("Cpu time in frequency state by policy id:");
+ writer.increaseIndent();
+ for (int i = 0; i < mTimeInStateByPolicyId.size(); i++) {
+ writer.printf("Policy id = %d, Time(millis) in state by CPU frequency(KHz) = %s\n",
+ mTimeInStateByPolicyId.keyAt(i), mTimeInStateByPolicyId.valueAt(i));
+ }
+ writer.decreaseIndent();
+
+ writer.println("Last read CPU infos:");
+ writer.increaseIndent();
+ for (int i = 0; i < mLastReadCpuInfos.size(); i++) {
+ writer.printf("%s\n", mLastReadCpuInfos.valueAt(i));
+ }
+ writer.decreaseIndent();
+
+ writer.println("Latest cumulative CPU usage stats by CPU core:");
+ writer.increaseIndent();
+ for (int i = 0; i < mCumulativeCpuUsageStats.size(); i++) {
+ writer.printf("CPU core id = %d, %s\n", mCumulativeCpuUsageStats.keyAt(i),
+ mCumulativeCpuUsageStats.valueAt(i));
+ }
+ writer.decreaseIndent();
+
+ writer.decreaseIndent(); // Remove intend for the outermost block.
+ }
+
/**
* Sets the CPU frequency for testing.
*
@@ -496,6 +587,9 @@
for (int i = 0; i < timeInState.size(); i++) {
totalTimeInState += timeInState.valueAt(i);
}
+ if (totalTimeInState == 0) {
+ return CpuInfo.MISSING_FREQUENCY;
+ }
double avgFreqKHz = 0;
for (int i = 0; i < timeInState.size(); i++) {
avgFreqKHz += (timeInState.keyAt(i) * timeInState.valueAt(i)) / totalTimeInState;
@@ -624,16 +718,29 @@
@CpusetCategory
public final int cpusetCategories;
public final boolean isOnline;
+ public final long maxCpuFreqKHz;
// Values in the below fields may be missing when a CPU core is offline.
public final long curCpuFreqKHz;
- public final long maxCpuFreqKHz;
public final long avgTimeInStateCpuFreqKHz;
@Nullable
public final CpuUsageStats latestCpuUsageStats;
+ private long mNormalizedAvailableCpuFreqKHz;
+
CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline,
long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
CpuUsageStats latestCpuUsageStats) {
+ this(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz,
+ avgTimeInStateCpuFreqKHz, /* normalizedAvailableCpuFreqKHz= */ 0,
+ latestCpuUsageStats);
+ this.mNormalizedAvailableCpuFreqKHz = computeNormalizedAvailableCpuFreqKHz();
+ }
+
+ // Should be used only for testing.
+ @VisibleForTesting
+ CpuInfo(int cpuCore, @CpusetCategory int cpusetCategories, boolean isOnline,
+ long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz,
+ long normalizedAvailableCpuFreqKHz, CpuUsageStats latestCpuUsageStats) {
this.cpuCore = cpuCore;
this.cpusetCategories = cpusetCategories;
this.isOnline = isOnline;
@@ -641,6 +748,11 @@
this.maxCpuFreqKHz = maxCpuFreqKHz;
this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz;
this.latestCpuUsageStats = latestCpuUsageStats;
+ this.mNormalizedAvailableCpuFreqKHz = normalizedAvailableCpuFreqKHz;
+ }
+
+ public long getNormalizedAvailableCpuFreqKHz() {
+ return mNormalizedAvailableCpuFreqKHz;
}
@Override
@@ -657,6 +769,8 @@
.append(avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY ? "missing"
: avgTimeInStateCpuFreqKHz)
.append(", latestCpuUsageStats = ").append(latestCpuUsageStats)
+ .append(", mNormalizedAvailableCpuFreqKHz = ")
+ .append(mNormalizedAvailableCpuFreqKHz)
.append(" }").toString();
}
@@ -673,13 +787,32 @@
&& isOnline == other.isOnline && curCpuFreqKHz == other.curCpuFreqKHz
&& maxCpuFreqKHz == other.maxCpuFreqKHz
&& avgTimeInStateCpuFreqKHz == other.avgTimeInStateCpuFreqKHz
- && latestCpuUsageStats.equals(other.latestCpuUsageStats);
+ && latestCpuUsageStats.equals(other.latestCpuUsageStats)
+ && mNormalizedAvailableCpuFreqKHz == other.mNormalizedAvailableCpuFreqKHz;
}
@Override
public int hashCode() {
return Objects.hash(cpuCore, cpusetCategories, isOnline, curCpuFreqKHz, maxCpuFreqKHz,
- avgTimeInStateCpuFreqKHz, latestCpuUsageStats);
+ avgTimeInStateCpuFreqKHz, latestCpuUsageStats, mNormalizedAvailableCpuFreqKHz);
+ }
+
+ private long computeNormalizedAvailableCpuFreqKHz() {
+ if (!isOnline) {
+ return MISSING_FREQUENCY;
+ }
+ long totalTimeMillis = latestCpuUsageStats.getTotalTimeMillis();
+ if (totalTimeMillis == 0) {
+ Slogf.wtf(TAG, "Total CPU time millis is 0. This shouldn't happen unless stats are"
+ + " polled too frequently");
+ return MISSING_FREQUENCY;
+ }
+ double nonIdlePercent = 100.0 * (totalTimeMillis
+ - (double) latestCpuUsageStats.idleTimeMillis) / totalTimeMillis;
+ long curFreqKHz = avgTimeInStateCpuFreqKHz == MISSING_FREQUENCY
+ ? curCpuFreqKHz : avgTimeInStateCpuFreqKHz;
+ double availablePercent = 100.0 - (nonIdlePercent * curFreqKHz / maxCpuFreqKHz);
+ return (long) ((availablePercent * maxCpuFreqKHz) / 100.0);
}
}
@@ -712,7 +845,7 @@
this.guestNiceTimeMillis = guestNiceTimeMillis;
}
- public long getTotalTime() {
+ public long getTotalTimeMillis() {
return userTimeMillis + niceTimeMillis + systemTimeMillis + idleTimeMillis
+ iowaitTimeMillis + irqTimeMillis + softirqTimeMillis + stealTimeMillis
+ guestTimeMillis + guestNiceTimeMillis;
@@ -796,8 +929,8 @@
@Override
public String toString() {
- return "FrequencyPair{cpuFreqKHz=" + cpuFreqKHz + ", scalingFreqKHz=" + scalingFreqKHz
- + '}';
+ return "FrequencyPair{cpuFreqKHz = " + cpuFreqKHz + ", scalingFreqKHz = "
+ + scalingFreqKHz + '}';
}
}
@@ -812,7 +945,7 @@
@Override
public String toString() {
- return "StaticPolicyInfo{maxCpuFreqPair=" + maxCpuFreqPair + ", relatedCpuCores="
+ return "StaticPolicyInfo{maxCpuFreqPair = " + maxCpuFreqPair + ", relatedCpuCores = "
+ relatedCpuCores + '}';
}
}
@@ -831,9 +964,9 @@
@Override
public String toString() {
- return "DynamicPolicyInfo{curCpuFreqPair=" + curCpuFreqPair
- + ", avgTimeInStateCpuFreqKHz=" + avgTimeInStateCpuFreqKHz
- + ", affectedCpuCores=" + affectedCpuCores + '}';
+ return "DynamicPolicyInfo{curCpuFreqPair = " + curCpuFreqPair
+ + ", avgTimeInStateCpuFreqKHz = " + avgTimeInStateCpuFreqKHz
+ + ", affectedCpuCores = " + affectedCpuCores + '}';
}
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 7b560ce..d57dc47 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -22,6 +22,7 @@
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
+import android.util.Slog;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.Surface;
@@ -39,6 +40,7 @@
* </p>
*/
abstract class DisplayDevice {
+ private static final String TAG = "DisplayDevice";
private static final Display.Mode EMPTY_DISPLAY_MODE = new Display.Mode.Builder().build();
private final DisplayAdapter mDisplayAdapter;
@@ -269,10 +271,13 @@
/**
* Sets the display layer stack while in a transaction.
*/
- public final void setLayerStackLocked(SurfaceControl.Transaction t, int layerStack) {
+ public final void setLayerStackLocked(SurfaceControl.Transaction t, int layerStack,
+ int layerStackTag) {
if (mCurrentLayerStack != layerStack) {
mCurrentLayerStack = layerStack;
t.setDisplayLayerStack(mDisplayToken, layerStack);
+ Slog.i(TAG, "[" + layerStackTag + "] Layerstack set to " + layerStack + " for "
+ + mUniqueId);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 214c591..21cc172 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2105,8 +2105,7 @@
}
}
- // TODO (b/264979880) - Add unit test for HDR output control methods.
- private void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
+ void setHdrConversionModeInternal(HdrConversionMode hdrConversionMode) {
if (!mInjector.getHdrOutputConversionSupport()) {
return;
}
@@ -2140,7 +2139,7 @@
}
}
- private HdrConversionMode getHdrConversionModeSettingInternal() {
+ HdrConversionMode getHdrConversionModeSettingInternal() {
if (!mInjector.getHdrOutputConversionSupport()) {
return HDR_CONVERSION_MODE_UNSUPPORTED;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index da8e6a6..84fe8f2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -885,7 +885,7 @@
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
final String brightnessThrottlingDataId =
mLogicalDisplay.getBrightnessThrottlingDataIdLocked();
- mHandler.post(() -> {
+ mHandler.postAtTime(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
changed = true;
@@ -916,7 +916,7 @@
if (changed) {
updatePowerState();
}
- });
+ }, mClock.uptimeMillis());
}
/**
@@ -940,10 +940,6 @@
mAutomaticBrightnessController.stop();
}
- if (mScreenOffBrightnessSensorController != null) {
- mScreenOffBrightnessSensorController.stop();
- }
-
if (mBrightnessSetting != null) {
mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
}
@@ -1190,6 +1186,7 @@
if (mScreenOffBrightnessSensorController != null) {
mScreenOffBrightnessSensorController.stop();
+ mScreenOffBrightnessSensorController = null;
}
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
@@ -1311,6 +1308,10 @@
mPowerState.stop();
mPowerState = null;
}
+
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.stop();
+ }
}
private void updatePowerState() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index a2a53e3..297a6f8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -729,7 +729,7 @@
final String brightnessThrottlingDataId =
mLogicalDisplay.getBrightnessThrottlingDataIdLocked();
- mHandler.post(() -> {
+ mHandler.postAtTime(() -> {
boolean changed = false;
if (mDisplayDevice != device) {
changed = true;
@@ -761,7 +761,7 @@
if (changed) {
updatePowerState();
}
- });
+ }, mClock.uptimeMillis());
}
/**
@@ -1028,6 +1028,10 @@
mBrightnessEventRingBuffer =
new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.stop();
+ mScreenOffBrightnessSensorController = null;
+ }
loadScreenOffBrightnessSensor();
int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
@@ -1133,6 +1137,10 @@
mPowerState.stop();
mPowerState = null;
}
+
+ if (mScreenOffBrightnessSensorController != null) {
+ mScreenOffBrightnessSensorController.stop();
+ }
}
private void updatePowerState() {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 78c597e..04532f9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -572,7 +572,7 @@
DisplayDevice device,
boolean isBlanked) {
// Set the layer stack.
- device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
+ device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack, mDisplayId);
// Also inform whether the device is the same one sent to inputflinger for its layerstack.
// Prevent displays that are disabled from receiving input.
// TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 1cc958b..fa2ba21 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -296,6 +296,8 @@
refreshAppOpsRestrictions(userId);
}
});
+ mInjector.getEmergencyHelper().addOnEmergencyStateChangedListener(
+ this::onEmergencyStateChanged);
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
@@ -567,6 +569,11 @@
refreshAppOpsRestrictions(userId);
}
+ private void onEmergencyStateChanged() {
+ boolean isInEmergency = mInjector.getEmergencyHelper().isInEmergency(Long.MIN_VALUE);
+ mInjector.getLocationUsageLogger().logEmergencyStateChanged(isInEmergency);
+ }
+
private void logLocationEnabledState() {
boolean locationEnabled = false;
// Location setting is considered on if it is enabled for any one user
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 77cd673..a081dff 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -92,6 +92,8 @@
// Represents an HAL interface version. Instances of this class are created in the JNI layer
// and returned through native methods.
static class HalInterfaceVersion {
+ // mMajor being this value denotes AIDL HAL. In this case, mMinor denotes the AIDL version.
+ static final int AIDL_INTERFACE = 3;
final int mMajor;
final int mMinor;
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 6c4c829..041f11d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -21,6 +21,7 @@
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.location.GnssMeasurementRequest;
@@ -31,6 +32,7 @@
import android.stats.location.LocationStatsEnums;
import android.util.Log;
+import com.android.server.location.gnss.GnssConfiguration.HalInterfaceVersion;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
@@ -115,16 +117,6 @@
if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) {
return true;
}
- // The HAL doc does not specify if consecutive start() calls will be allowed.
- // Some vendors may ignore the 2nd start() call if stop() is not called.
- // Thus, here we always call stop() before calling start() to avoid being ignored.
- if (mGnssNative.stopMeasurementCollection()) {
- if (D) {
- Log.d(TAG, "stopping gnss measurements");
- }
- } else {
- Log.e(TAG, "error stopping gnss measurements");
- }
if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
request.isCorrelationVectorOutputsEnabled(),
request.getIntervalMillis())) {
@@ -139,6 +131,28 @@
}
@Override
+ protected boolean reregisterWithService(GnssMeasurementRequest old,
+ GnssMeasurementRequest request,
+ @NonNull Collection<GnssListenerRegistration> registrations) {
+ if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) {
+ unregisterWithService();
+ return true;
+ }
+ HalInterfaceVersion halInterfaceVersion =
+ mGnssNative.getConfiguration().getHalInterfaceVersion();
+ boolean aidlV3Plus = halInterfaceVersion.mMajor == HalInterfaceVersion.AIDL_INTERFACE
+ && halInterfaceVersion.mMinor >= 3;
+ if (!aidlV3Plus) {
+ // The HAL doc does not specify if consecutive start() calls will be allowed.
+ // Some vendors may ignore the 2nd start() call if stop() is not called.
+ // Thus, here we always call stop() before calling start() to avoid being ignored.
+ // AIDL v3+ is free from this issue.
+ unregisterWithService();
+ }
+ return registerWithService(request, registrations);
+ }
+
+ @Override
protected void unregisterWithService() {
if (mGnssNative.stopMeasurementCollection()) {
if (D) {
diff --git a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
index f5114b7..01c108b 100644
--- a/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NetworkTimeHelper.java
@@ -37,7 +37,7 @@
* a platform bug. This switch will be removed in a future release. If there are problems with
* the new impl we'd like to hear about them.
*/
- static final boolean USE_TIME_DETECTOR_IMPL = false;
+ static final boolean USE_TIME_DETECTOR_IMPL = true;
/**
* The callback interface used by {@link NetworkTimeHelper} to report the time to {@link
diff --git a/services/core/java/com/android/server/location/injector/EmergencyHelper.java b/services/core/java/com/android/server/location/injector/EmergencyHelper.java
index be4bf50..10cf714 100644
--- a/services/core/java/com/android/server/location/injector/EmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/EmergencyHelper.java
@@ -16,14 +16,55 @@
package com.android.server.location.injector;
+import java.util.concurrent.CopyOnWriteArrayList;
+
/**
* Provides helpers for emergency sessions.
*/
public abstract class EmergencyHelper {
+ private final CopyOnWriteArrayList<EmergencyStateChangedListener> mListeners;
+
+ protected EmergencyHelper() {
+ mListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /**
+ * Listener for emergency state changes.
+ */
+ public interface EmergencyStateChangedListener {
+ /**
+ * Called when state changes.
+ */
+ void onStateChanged();
+ }
+
/**
* Returns true if the device is in an emergency session, or if an emergency session ended
* within the given extension time.
*/
public abstract boolean isInEmergency(long extensionTimeMs);
+
+ /**
+ * Add a listener for changes to the emergency location state.
+ */
+ public void addOnEmergencyStateChangedListener(EmergencyStateChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener for changes to the emergency location state.
+ */
+ public void removeOnEmergencyStateChangedListener(EmergencyStateChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Notify listeners for emergency state of state change
+ */
+ protected final void dispatchEmergencyStateChanged() {
+ for (EmergencyStateChangedListener listener : mListeners) {
+ listener.onStateChanged();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index b2c8672..4a0c4b2 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -16,13 +16,11 @@
package com.android.server.location.injector;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.location.settings.LocationSettings;
/**
* Injects various location dependencies so that they may be controlled by tests.
*/
-@VisibleForTesting
public interface Injector {
/** Returns a UserInfoHelper. */
diff --git a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
index a9701b3..9319e89 100644
--- a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
@@ -129,6 +129,13 @@
FrameworkStatsLog.write(FrameworkStatsLog.LOCATION_ENABLED_STATE_CHANGED, enabled);
}
+ /**
+ * Log emergency location state change event
+ */
+ public synchronized void logEmergencyStateChanged(boolean isInEmergency) {
+ FrameworkStatsLog.write(FrameworkStatsLog.EMERGENCY_STATE_CHANGED, isInEmergency);
+ }
+
private static int bucketizeProvider(String provider) {
if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
return LocationStatsEnums.PROVIDER_NETWORK;
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 1fb00ef..c772e08 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -27,6 +27,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.server.FgThread;
import java.util.Objects;
@@ -73,12 +74,25 @@
try {
mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ dispatchEmergencyStateChanged();
} catch (IllegalStateException e) {
Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
}
}
}
}, new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL));
+
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(
+ intent.getAction())) {
+ return;
+ }
+
+ dispatchEmergencyStateChanged();
+ }
+ }, new IntentFilter(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED));
}
@Override
@@ -108,6 +122,7 @@
if (mIsInEmergencyCall) {
mEmergencyCallEndRealtimeMs = SystemClock.elapsedRealtime();
mIsInEmergencyCall = false;
+ dispatchEmergencyStateChanged();
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 925ab65..7aaf915 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -64,6 +64,7 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.altitude.AltitudeConverter;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -81,6 +82,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.stats.location.LocationStatsEnums;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -94,6 +96,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
+import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
@@ -122,6 +125,7 @@
import com.android.server.location.settings.LocationUserSettings;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1441,6 +1445,10 @@
@GuardedBy("mMultiplexerLock")
@Nullable private StateChangedListener mStateChangedListener;
+ /** Enables missing MSL altitudes to be added on behalf of the provider. */
+ private final AltitudeConverter mAltitudeConverter = new AltitudeConverter();
+ private volatile boolean mIsAltitudeConverterIdle = true;
+
public LocationProviderManager(Context context, Injector injector,
String name, @Nullable PassiveLocationProviderManager passiveManager) {
this(context, injector, name, passiveManager, Collections.emptyList());
@@ -2512,33 +2520,18 @@
@GuardedBy("mMultiplexerLock")
@Override
public void onReportLocation(LocationResult locationResult) {
- LocationResult filtered;
+ LocationResult processed;
if (mPassiveManager != null) {
- filtered = locationResult.filter(location -> {
- if (!location.isMock()) {
- if (location.getLatitude() == 0 && location.getLongitude() == 0) {
- Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
- return false;
- }
- }
-
- if (!location.isComplete()) {
- Log.e(TAG, "blocking incomplete location from " + mName + " provider");
- return false;
- }
-
- return true;
- });
-
- if (filtered == null) {
+ processed = processReportedLocation(locationResult);
+ if (processed == null) {
return;
}
// don't log location received for passive provider because it's spammy
- EVENT_LOG.logProviderReceivedLocations(mName, filtered.size());
+ EVENT_LOG.logProviderReceivedLocations(mName, processed.size());
} else {
- // passive provider should get already filtered results as input
- filtered = locationResult;
+ // passive provider should get already processed results as input
+ processed = locationResult;
}
// check for non-monotonic locations if we're not the passive manager. the passive manager
@@ -2554,20 +2547,78 @@
}
// update last location
- setLastLocation(filtered.getLastLocation(), UserHandle.USER_ALL);
+ setLastLocation(processed.getLastLocation(), UserHandle.USER_ALL);
// attempt listener delivery
deliverToListeners(registration -> {
- return registration.acceptLocationChange(filtered);
+ return registration.acceptLocationChange(processed);
});
// notify passive provider
if (mPassiveManager != null) {
- mPassiveManager.updateLocation(filtered);
+ mPassiveManager.updateLocation(processed);
}
}
@GuardedBy("mMultiplexerLock")
+ @Nullable
+ private LocationResult processReportedLocation(LocationResult locationResult) {
+ LocationResult processed = locationResult.filter(location -> {
+ if (!location.isMock()) {
+ if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+ Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
+ return false;
+ }
+ }
+
+ if (!location.isComplete()) {
+ Log.e(TAG, "blocking incomplete location from " + mName + " provider");
+ return false;
+ }
+
+ return true;
+ });
+ if (processed == null) {
+ return null;
+ }
+
+ // Attempt to add a missing MSL altitude on behalf of the provider.
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", false)) {
+ return processed.map(location -> {
+ if (!location.hasMslAltitude() && location.hasAltitude()) {
+ try {
+ Location locationCopy = new Location(location);
+ if (mAltitudeConverter.addMslAltitudeToLocation(locationCopy)) {
+ return locationCopy;
+ }
+ // Only queue up one IO thread runnable.
+ if (mIsAltitudeConverterIdle) {
+ mIsAltitudeConverterIdle = false;
+ IoThread.getExecutor().execute(() -> {
+ try {
+ // Results added to the location copy are essentially discarded.
+ // We only rely on the side effect of loading altitude assets
+ // into the converter's memory cache.
+ mAltitudeConverter.addMslAltitudeToLocation(mContext,
+ locationCopy);
+ } catch (IOException e) {
+ Log.e(TAG, "not loading MSL altitude assets: " + e);
+ }
+ mIsAltitudeConverterIdle = true;
+ });
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "not adding MSL altitude to location: " + e);
+ }
+ }
+ return location;
+ });
+ }
+ return processed;
+ }
+
+ @GuardedBy("mMultiplexerLock")
private void onUserStarted(int userId) {
if (userId == UserHandle.USER_NULL) {
return;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 5a832b7..38fd69f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -33,7 +33,6 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -381,7 +380,6 @@
*/
private void tieProfileLockIfNecessary(int profileUserId,
LockscreenCredential profileUserPassword) {
- if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + profileUserId);
// Only for profiles that shares credential with parent
if (!isCredentialSharableWithParent(profileUserId)) {
return;
@@ -399,8 +397,7 @@
// as its parent.
final int parentId = mUserManager.getProfileParent(profileUserId).id;
if (!isUserSecure(parentId) && !profileUserPassword.isNone()) {
- if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock but profile has one");
-
+ Slogf.i(TAG, "Clearing password for profile user %d to match parent", profileUserId);
setLockCredentialInternal(LockscreenCredential.createNone(), profileUserPassword,
profileUserId, /* isLockTiedToParent= */ true);
return;
@@ -416,7 +413,6 @@
Slog.e(TAG, "Failed to talk to GateKeeper service", e);
return;
}
- if (DEBUG) Slog.v(TAG, "Tie profile to parent now!");
try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) {
setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId,
/* isLockTiedToParent= */ true);
@@ -888,7 +884,6 @@
&& !getBoolean("migrated_frp", false, 0)) {
migrateFrpCredential();
setBoolean("migrated_frp", true, 0);
- Slog.i(TAG, "Migrated migrated_frp.");
}
}
@@ -1269,7 +1264,6 @@
}
private void unlockKeystore(byte[] password, int userHandle) {
- if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
Authorization.onLockScreenEvent(false, userHandle, password, null);
}
@@ -1279,7 +1273,7 @@
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
CertificateException, IOException {
- if (DEBUG) Slog.v(TAG, "Get child profile decrypted key");
+ Slogf.d(TAG, "Decrypting password for tied profile %d", userId);
byte[] storedData = mStorage.readChildProfileLock(userId);
if (storedData == null) {
throw new FileNotFoundException("Child profile lock file not found");
@@ -1328,7 +1322,6 @@
* {@link com.android.server.SystemServiceManager#unlockUser} </em>
*/
private void unlockUser(@UserIdInt int userId) {
- Slogf.i(TAG, "Unlocking user %d", userId);
// TODO: make this method fully async so we can update UI with progress strings
final boolean alreadyUnlocked = mUserManager.isUserUnlockingOrUnlocked(userId);
final CountDownLatch latch = new CountDownLatch(1);
@@ -1638,7 +1631,6 @@
LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) {
Objects.requireNonNull(credential);
Objects.requireNonNull(savedCredential);
- if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
synchronized (mSpManager) {
if (savedCredential.isNone() && isProfileWithUnifiedLock(userId)) {
// get credential from keystore when profile has unified lock
@@ -1720,6 +1712,7 @@
if (passwordHistoryLength == 0) {
passwordHistory = "";
} else {
+ Slogf.d(TAG, "Adding new password to password history for user %d", userHandle);
final byte[] hashFactor = getHashFactor(password, userHandle);
final byte[] salt = getSalt(userHandle).getBytes();
String hash = password.passwordToHistoryHash(salt, hashFactor);
@@ -1751,7 +1744,6 @@
if (salt == 0) {
salt = SecureRandomUtils.randomLong();
setLong(LockPatternUtils.LOCK_PASSWORD_SALT_KEY, salt, userId);
- Slog.v(TAG, "Initialized lock password salt for user: " + userId);
}
return Long.toHexString(salt);
}
@@ -1875,7 +1867,8 @@
@VisibleForTesting /** Note: this method is overridden in unit tests */
protected void tieProfileLockToParent(int profileUserId, int parentUserId,
LockscreenCredential password) {
- if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + profileUserId);
+ Slogf.i(TAG, "Tying lock for profile user %d to parent user %d", profileUserId,
+ parentUserId);
final byte[] iv;
final byte[] ciphertext;
final long parentSid;
@@ -2132,7 +2125,7 @@
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
- Slog.d(TAG, "doVerifyCredential: user=" + userId);
+ Slogf.i(TAG, "Verifying lockscreen credential for user %d", userId);
final AuthenticationResult authResult;
VerifyCredentialResponse response;
@@ -2152,20 +2145,10 @@
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.syntheticPassword.deriveGkPassword());
-
- // perform verifyChallenge with synthetic password which generates the real GK auth
- // token and response for the current user
- response = mSpManager.verifyChallenge(getGateKeeperService(),
- authResult.syntheticPassword, 0L /* challenge */, userId);
- if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
- // This shouldn't really happen: the unwrapping of SP succeeds, but SP doesn't
- // match the recorded GK password handle.
- Slog.wtf(TAG, "verifyChallenge with SP failed.");
- return VerifyCredentialResponse.ERROR;
- }
}
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ Slogf.i(TAG, "Successfully verified lockscreen credential for user %d", userId);
onCredentialVerified(authResult.syntheticPassword,
PasswordMetrics.computeForCredential(credential), userId);
if ((flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0) {
@@ -2324,13 +2307,18 @@
}
private void removeKeystoreProfileKey(int targetUserId) {
- Slog.i(TAG, "Remove keystore profile key for user: " + targetUserId);
+ final String encryptAlias = PROFILE_KEY_NAME_ENCRYPT + targetUserId;
+ final String decryptAlias = PROFILE_KEY_NAME_DECRYPT + targetUserId;
try {
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
+ if (mJavaKeyStore.containsAlias(encryptAlias) ||
+ mJavaKeyStore.containsAlias(decryptAlias)) {
+ Slogf.i(TAG, "Removing keystore profile key for user %d", targetUserId);
+ mJavaKeyStore.deleteEntry(encryptAlias);
+ mJavaKeyStore.deleteEntry(decryptAlias);
+ }
} catch (KeyStoreException e) {
- // We have tried our best to remove all keys
- Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
+ // We have tried our best to remove the key.
+ Slogf.e(TAG, e, "Error removing keystore profile key for user %d", targetUserId);
}
}
@@ -2678,7 +2666,7 @@
@VisibleForTesting
SyntheticPassword initializeSyntheticPassword(int userId) {
synchronized (mSpManager) {
- Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
+ Slogf.i(TAG, "Initializing synthetic password for user %d", userId);
Preconditions.checkState(getCurrentLskfBasedProtectorId(userId) ==
SyntheticPasswordManager.NULL_PROTECTOR_ID,
"Cannot reinitialize SP");
@@ -2689,6 +2677,7 @@
setCurrentLskfBasedProtectorId(protectorId, userId);
setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
onSyntheticPasswordCreated(userId, sp);
+ Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
}
}
@@ -2725,8 +2714,11 @@
final long finalHandle = handle;
mHandler.postDelayed(() -> {
synchronized (mGatekeeperPasswords) {
- Slog.d(TAG, "Removing handle: " + finalHandle);
- mGatekeeperPasswords.remove(finalHandle);
+ if (mGatekeeperPasswords.get(finalHandle) != null) {
+ Slogf.d(TAG, "Cached Gatekeeper password with handle %016x has expired",
+ finalHandle);
+ mGatekeeperPasswords.remove(finalHandle);
+ }
}
}, GK_PW_HANDLE_STORE_DURATION_MS);
@@ -2769,13 +2761,14 @@
*
* Also maintains the invariants described in {@link SyntheticPasswordManager} by
* setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the
- * LSKF is added/removed, respectively. If the new LSKF is nonempty, then the Gatekeeper auth
+ * LSKF is added/removed, respectively. If an LSKF is being added, then the Gatekeeper auth
* token is also refreshed.
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
SyntheticPassword sp, int userId) {
- if (DEBUG) Slog.d(TAG, "setLockCredentialWithSpLocked: user=" + userId);
+ Slogf.i(TAG, "Changing lockscreen credential of user %d; newCredentialType=%s\n",
+ userId, LockPatternUtils.credentialTypeToString(credential.getType()));
final int savedCredentialType = getCredentialTypeInternal(userId);
final long oldProtectorId = getCurrentLskfBasedProtectorId(userId);
final long newProtectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
@@ -2785,9 +2778,7 @@
// not needed by synchronizeUnifiedWorkChallengeForProfiles()
profilePasswords = null;
- if (mSpManager.hasSidForUser(userId)) {
- mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
- } else {
+ if (!mSpManager.hasSidForUser(userId)) {
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
setKeystorePassword(sp.deriveKeyStorePassword(), userId);
@@ -2820,6 +2811,7 @@
}
}
mSpManager.destroyLskfBasedProtector(oldProtectorId, userId);
+ Slogf.i(TAG, "Successfully changed lockscreen credential of user %d", userId);
return newProtectorId;
}
@@ -2904,6 +2896,7 @@
public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
checkPasswordReadPermission();
try {
+ Slogf.d(TAG, "Getting password history hash factor for user %d", userId);
if (isProfileWithUnifiedLock(userId)) {
try {
currentCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -2929,7 +2922,7 @@
private long addEscrowToken(@NonNull byte[] token, @TokenType int type, int userId,
@NonNull EscrowTokenStateChangeCallback callback) {
- if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId + ", type=" + type);
+ Slogf.i(TAG, "Adding escrow token for user %d", userId);
synchronized (mSpManager) {
// If the user has no LSKF, then the token can be activated immediately. Otherwise, the
// token can't be activated until the SP is unlocked by another protector (normally the
@@ -2947,18 +2940,20 @@
long handle = mSpManager.addPendingToken(token, type, userId, callback);
if (sp != null) {
// Activate the token immediately
+ Slogf.i(TAG, "Immediately activating escrow token %016x", handle);
mSpManager.createTokenBasedProtector(handle, sp, userId);
+ } else {
+ Slogf.i(TAG, "Escrow token %016x will be activated when user is unlocked", handle);
}
return handle;
}
}
private void activateEscrowTokens(SyntheticPassword sp, int userId) {
- if (DEBUG) Slog.d(TAG, "activateEscrowTokens: user=" + userId);
synchronized (mSpManager) {
disableEscrowTokenOnNonManagedDevicesIfNeeded(userId);
for (long handle : mSpManager.getPendingTokensForUser(userId)) {
- Slog.i(TAG, TextUtils.formatSimple("activateEscrowTokens: %x %d ", handle, userId));
+ Slogf.i(TAG, "Activating escrow token %016x for user %d", handle, userId);
mSpManager.createTokenBasedProtector(handle, sp, userId);
}
}
@@ -3029,6 +3024,8 @@
@GuardedBy("mSpManager")
private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
long tokenHandle, byte[] token, int userId) {
+ Slogf.i(TAG, "Resetting lockscreen credential of user %d using escrow token %016x",
+ userId, tokenHandle);
final AuthenticationResult result;
result = mSpManager.unlockTokenBasedProtector(getGateKeeperService(), tokenHandle, token,
userId);
@@ -3051,8 +3048,9 @@
private boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId) {
AuthenticationResult authResult;
synchronized (mSpManager) {
+ Slogf.i(TAG, "Unlocking user %d using escrow token %016x", userId, tokenHandle);
if (!mSpManager.hasEscrowData(userId)) {
- Slog.w(TAG, "Escrow token is disabled on the current user");
+ Slogf.w(TAG, "Escrow token support is disabled on user %d", userId);
return false;
}
authResult = mSpManager.unlockTokenBasedProtector(getGateKeeperService(), tokenHandle,
@@ -3063,6 +3061,7 @@
}
}
+ Slogf.i(TAG, "Unlocked synthetic password for user %d using escrow token", userId);
onCredentialVerified(authResult.syntheticPassword,
loadPasswordMetrics(authResult.syntheticPassword, userId), userId);
return true;
@@ -3090,21 +3089,6 @@
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp));
}
- private static String credentialTypeToString(int credentialType) {
- switch (credentialType) {
- case CREDENTIAL_TYPE_NONE:
- return "None";
- case CREDENTIAL_TYPE_PATTERN:
- return "Pattern";
- case CREDENTIAL_TYPE_PIN:
- return "Pin";
- case CREDENTIAL_TYPE_PASSWORD:
- return "Password";
- default:
- return "Unknown " + credentialType;
- }
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
@@ -3121,22 +3105,23 @@
pw.println("User " + userId);
pw.increaseIndent();
synchronized (mSpManager) {
- pw.println(TextUtils.formatSimple("LSKF-based SP protector ID: %x",
+ pw.println(TextUtils.formatSimple("LSKF-based SP protector ID: %016x",
getCurrentLskfBasedProtectorId(userId)));
- pw.println(TextUtils.formatSimple("LSKF last changed: %s (previous protector: %x)",
- timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
- getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
+ pw.println(TextUtils.formatSimple(
+ "LSKF last changed: %s (previous protector: %016x)",
+ timestampToString(getLong(LSKF_LAST_CHANGED_TIME_KEY, 0, userId)),
+ getLong(PREV_LSKF_BASED_PROTECTOR_ID_KEY, 0, userId)));
}
try {
- pw.println(TextUtils.formatSimple("SID: %x",
+ pw.println(TextUtils.formatSimple("SID: %016x",
getGateKeeperService().getSecureUserId(userId)));
} catch (RemoteException e) {
// ignore.
}
- // It's OK to dump the password type since anyone with physical access can just
+ // It's OK to dump the credential type since anyone with physical access can just
// observe it from the keyguard directly.
pw.println("Quality: " + getKeyguardStoredQuality(userId));
- pw.println("CredentialType: " + credentialTypeToString(
+ pw.println("CredentialType: " + LockPatternUtils.credentialTypeToString(
getCredentialTypeInternal(userId)));
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabledInternal(userId));
pw.println(TextUtils.formatSimple("Metrics: %s",
@@ -3194,6 +3179,11 @@
* if we are running an automotive build.
*/
private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
+
+ if (!mSpManager.hasAnyEscrowData(userId)) {
+ return;
+ }
+
// TODO(b/258213147): Remove
final long identity = Binder.clearCallingIdentity();
try {
@@ -3238,7 +3228,7 @@
}
// Disable escrow token permanently on all other device/user types.
- Slog.i(TAG, "Disabling escrow token on user " + userId);
+ Slogf.i(TAG, "Permanently disabling support for escrow tokens on user %d", userId);
mSpManager.destroyEscrowData(userId);
}
@@ -3470,6 +3460,7 @@
synchronized (mSpManager) {
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
}
+ Slogf.i(TAG, "Restored synthetic password for user %d using reboot escrow", userId);
onCredentialVerified(sp, loadPasswordMetrics(sp, userId), userId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d070b41..1663b01 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -744,6 +744,11 @@
&& hasState(SP_P1_NAME, NULL_PROTECTOR_ID, userId);
}
+ public boolean hasAnyEscrowData(int userId) {
+ return hasState(SP_E0_NAME, NULL_PROTECTOR_ID, userId)
+ || hasState(SP_P1_NAME, NULL_PROTECTOR_ID, userId);
+ }
+
public void destroyEscrowData(int userId) {
destroyState(SP_E0_NAME, NULL_PROTECTOR_ID, userId);
destroyState(SP_P1_NAME, NULL_PROTECTOR_ID, userId);
@@ -786,11 +791,11 @@
}
Set<Integer> usedSlots = getUsedWeaverSlots();
if (!usedSlots.contains(slot)) {
- Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
+ Slogf.i(TAG, "Erasing Weaver slot %d", slot);
weaverEnroll(slot, null, null);
mPasswordSlotManager.markSlotDeleted(slot);
} else {
- Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
+ Slogf.i(TAG, "Weaver slot %d was already reused; not erasing it", slot);
}
}
}
@@ -858,11 +863,13 @@
long sid = GateKeeper.INVALID_SECURE_USER_ID;
final byte[] protectorSecret;
+ Slogf.i(TAG, "Creating LSKF-based protector %016x for user %d", protectorId, userId);
+
if (isWeaverAvailable()) {
// Weaver is available, so make the protector use it to verify the LSKF. Do this even
// if the LSKF is empty, as that gives us support for securely deleting the protector.
int weaverSlot = getNextAvailableWeaverSlot();
- Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
+ Slogf.i(TAG, "Enrolling LSKF for user %d into Weaver slot %d", userId, weaverSlot);
byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
null);
if (weaverSecret == null) {
@@ -892,6 +899,7 @@
} catch (RemoteException ignore) {
Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
+ Slogf.i(TAG, "Enrolling LSKF for user %d into Gatekeeper", userId);
GateKeeperResponse response;
try {
response = gatekeeper.enroll(fakeUserId(userId), null, null,
@@ -964,6 +972,7 @@
&& LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)
&& getCredentialType(protectorId, userInfo.id) !=
LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ Slog.i(TAG, "Migrating FRP credential to persistent data block");
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, protectorId,
userInfo.id));
int weaverSlot = loadWeaverSlot(protectorId, userInfo.id);
@@ -1092,9 +1101,10 @@
Slog.w(TAG, "User is not escrowable");
return false;
}
+ Slogf.i(TAG, "Creating token-based protector %016x for user %d", tokenHandle, userId);
if (isWeaverAvailable()) {
int slot = getNextAvailableWeaverSlot();
- Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
+ Slogf.i(TAG, "Using Weaver slot %d for new token-based protector", slot);
if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
Slog.e(TAG, "Failed to enroll weaver secret when activating token");
return false;
@@ -1170,8 +1180,9 @@
storedType = pwd.credentialType;
}
if (!credential.checkAgainstStoredType(storedType)) {
- Slog.e(TAG, TextUtils.formatSimple("Credential type mismatch: expected %d actual %d",
- storedType, credential.getType()));
+ Slogf.e(TAG, "Credential type mismatch: stored type is %s but provided type is %s",
+ LockPatternUtils.credentialTypeToString(storedType),
+ LockPatternUtils.credentialTypeToString(credential.getType()));
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
@@ -1473,6 +1484,7 @@
/** Destroy a token-based SP protector. */
public void destroyTokenBasedProtector(long protectorId, int userId) {
+ Slogf.i(TAG, "Destroying token-based protector %016x for user %d", protectorId, userId);
SyntheticPasswordBlob blob = SyntheticPasswordBlob.fromBytes(loadState(SP_BLOB_NAME,
protectorId, userId));
destroyProtectorCommon(protectorId, userId);
@@ -1498,6 +1510,7 @@
* Destroy an LSKF-based SP protector. This is used when the user's LSKF is changed.
*/
public void destroyLskfBasedProtector(long protectorId, int userId) {
+ Slogf.i(TAG, "Destroying LSKF-based protector %016x for user %d", protectorId, userId);
destroyProtectorCommon(protectorId, userId);
destroyState(PASSWORD_DATA_NAME, protectorId, userId);
destroyState(PASSWORD_METRICS_NAME, protectorId, userId);
@@ -1658,6 +1671,9 @@
}
private String getProtectorKeyAlias(long protectorId) {
+ // Note, this arguably has a bug: %x should be %016x so that the protector ID is left-padded
+ // with zeroes, like how the synthetic password state files are named. It's too late to fix
+ // this, though, and it doesn't actually matter.
return TextUtils.formatSimple("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
}
diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
index 58fdb57..eb997ba 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
+++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -18,7 +18,6 @@
import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
-import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,18 +36,18 @@
import android.media.MediaRoute2Info;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -61,58 +60,70 @@
* <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
* Policies.
*/
-class AudioPoliciesBluetoothRouteController implements BluetoothRouteController {
+/* package */ class AudioPoliciesBluetoothRouteController
+ implements BluetoothRouteController {
private static final String TAG = "APBtRouteController";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
- // Maps hardware address to BluetoothRouteInfo
- private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
- private final List<BluetoothRouteInfo> mActiveRoutes = new ArrayList<>();
-
- // Route type -> volume map
- private final SparseIntArray mVolumeMap = new SparseIntArray();
-
- private final Context mContext;
- private final BluetoothAdapter mBluetoothAdapter;
- private final BluetoothRoutesUpdatedListener mListener;
- private final AudioManager mAudioManager;
- private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
-
+ @NonNull
private final AdapterStateChangedReceiver mAdapterStateChangedReceiver =
new AdapterStateChangedReceiver();
+
+ @NonNull
private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
new DeviceStateChangedReceiver();
- private BluetoothA2dp mA2dpProfile;
- private BluetoothHearingAid mHearingAidProfile;
- private BluetoothLeAudio mLeAudioProfile;
+ @NonNull
+ private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
- AudioPoliciesBluetoothRouteController(Context context, BluetoothAdapter btAdapter,
- BluetoothRoutesUpdatedListener listener) {
- mContext = context;
- mBluetoothAdapter = btAdapter;
- mListener = listener;
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- buildBluetoothRoutes();
+ @NonNull
+ private final SparseIntArray mVolumeMap = new SparseIntArray();
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BluetoothAdapter mBluetoothAdapter;
+ @NonNull
+ private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
+ @NonNull
+ private final BluetoothProfileMonitor mBluetoothProfileMonitor;
+ @NonNull
+ private final AudioManager mAudioManager;
+
+ @Nullable
+ private BluetoothRouteInfo mSelectedBluetoothRoute;
+
+ AudioPoliciesBluetoothRouteController(@NonNull Context context,
+ @NonNull BluetoothAdapter bluetoothAdapter,
+ @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
+ this(context, bluetoothAdapter,
+ new BluetoothProfileMonitor(context, bluetoothAdapter), listener);
}
- /**
- * Registers listener to bluetooth status changes as the provided user.
- *
- * The registered receiver listens to {@link BluetoothA2dp#ACTION_ACTIVE_DEVICE_CHANGED} and
- * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED } events for {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#HEARING_AID}, and {@link BluetoothProfile#LE_AUDIO} bluetooth profiles.
- *
- * @param user {@code UserHandle} as which receiver is registered
- */
+ @VisibleForTesting
+ AudioPoliciesBluetoothRouteController(@NonNull Context context,
+ @NonNull BluetoothAdapter bluetoothAdapter,
+ @NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
+ @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(bluetoothAdapter);
+ Objects.requireNonNull(bluetoothProfileMonitor);
+ Objects.requireNonNull(listener);
+
+ mContext = context;
+ mBluetoothAdapter = bluetoothAdapter;
+ mBluetoothProfileMonitor = bluetoothProfileMonitor;
+ mAudioManager = mContext.getSystemService(AudioManager.class);
+ mListener = listener;
+
+ updateBluetoothRoutes();
+ }
+
@Override
public void start(UserHandle user) {
- mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
- mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
- mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
+ mBluetoothProfileMonitor.start();
IntentFilter adapterStateChangedIntentFilter = new IntentFilter();
@@ -143,22 +154,59 @@
}
@Override
- public boolean selectRoute(String deviceAddress) {
- // Temporary no-op.
- return false;
+ public boolean selectRoute(@Nullable String deviceAddress) {
+ synchronized (this) {
+ // Fetch all available devices in order to avoid race conditions with Bluetooth stack.
+ updateBluetoothRoutes();
+
+ if (deviceAddress == null) {
+ mSelectedBluetoothRoute = null;
+ return true;
+ }
+
+ BluetoothRouteInfo bluetoothRouteInfo = mBluetoothRoutes.get(deviceAddress);
+
+ if (bluetoothRouteInfo == null) {
+ Slog.w(TAG, "Cannot find bluetooth route for " + deviceAddress);
+ return false;
+ }
+
+ mSelectedBluetoothRoute = bluetoothRouteInfo;
+ setRouteConnectionState(mSelectedBluetoothRoute, STATE_CONNECTED);
+
+ updateConnectivityStateForDevicesInTheSameGroup();
+
+ return true;
+ }
}
/**
- * Transfers to a given bluetooth route.
- * The dedicated BT device with the route would be activated.
+ * Updates connectivity state for devices in the same devices group.
*
- * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
- * BT routes.
+ * <p>{@link BluetoothProfile#LE_AUDIO} and {@link BluetoothProfile#HEARING_AID} support
+ * grouping devices. Devices that belong to the same group should have the same routeId but
+ * different physical address.
+ *
+ * <p>In case one of the devices from the group is selected then other devices should also
+ * reflect this by changing their connectivity status to
+ * {@link MediaRoute2Info#CONNECTION_STATE_CONNECTED}.
*/
+ private void updateConnectivityStateForDevicesInTheSameGroup() {
+ synchronized (this) {
+ for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ if (TextUtils.equals(btRoute.mRoute.getId(), mSelectedBluetoothRoute.mRoute.getId())
+ && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
+ mSelectedBluetoothRoute.mBtDevice.getAddress())) {
+ setRouteConnectionState(btRoute, STATE_CONNECTED);
+ }
+ }
+ }
+ }
+
@Override
public void transferTo(@Nullable String routeId) {
if (routeId == null) {
- clearActiveDevices();
+ mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
return;
}
@@ -169,38 +217,38 @@
return;
}
- if (mBluetoothAdapter != null) {
- mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
- }
+ mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
}
- private BluetoothRouteInfo findBluetoothRouteWithRouteId(String routeId) {
+ @Nullable
+ private BluetoothRouteInfo findBluetoothRouteWithRouteId(@Nullable String routeId) {
if (routeId == null) {
return null;
}
- for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
- return btRouteInfo;
+ synchronized (this) {
+ for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
+ if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
+ return btRouteInfo;
+ }
}
}
return null;
}
- /**
- * Clears the active device for all known profiles.
- */
- private void clearActiveDevices() {
- if (mBluetoothAdapter != null) {
- mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
- }
- }
-
- private void buildBluetoothRoutes() {
- mBluetoothRoutes.clear();
+ private void updateBluetoothRoutes() {
Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
- if (bondedDevices != null) {
+
+ if (bondedDevices == null) {
+ return;
+ }
+
+ synchronized (this) {
+ mBluetoothRoutes.clear();
+
+ // We need to query all available to BT stack devices in order to avoid inconsistency
+ // between external services, like, AndroidManager, and BT stack.
for (BluetoothDevice device : bondedDevices) {
- if (device.isConnected()) {
+ if (isDeviceConnected(device)) {
BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
if (newBtRoute.mConnectedProfiles.size() > 0) {
mBluetoothRoutes.put(device.getAddress(), newBtRoute);
@@ -210,20 +258,31 @@
}
}
+ @VisibleForTesting
+ /* package */ boolean isDeviceConnected(@NonNull BluetoothDevice device) {
+ return device.isConnected();
+ }
+
@Nullable
@Override
public MediaRoute2Info getSelectedRoute() {
- // For now, active routes can be multiple only when a pair of hearing aid devices is active.
- // Let the first active device represent them.
- return (mActiveRoutes.isEmpty() ? null : mActiveRoutes.get(0).mRoute);
+ synchronized (this) {
+ if (mSelectedBluetoothRoute == null) {
+ return null;
+ }
+
+ return mSelectedBluetoothRoute.mRoute;
+ }
}
@NonNull
@Override
public List<MediaRoute2Info> getTransferableRoutes() {
List<MediaRoute2Info> routes = getAllBluetoothRoutes();
- for (BluetoothRouteInfo btRoute : mActiveRoutes) {
- routes.remove(btRoute.mRoute);
+ synchronized (this) {
+ if (mSelectedBluetoothRoute != null) {
+ routes.remove(mSelectedBluetoothRoute.mRoute);
+ }
}
return routes;
}
@@ -240,22 +299,19 @@
routeIds.add(selectedRoute.getId());
}
- for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- // A pair of hearing aid devices or having the same hardware address
- if (routeIds.contains(btRoute.mRoute.getId())) {
- continue;
+ synchronized (this) {
+ for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
+ // A pair of hearing aid devices or having the same hardware address
+ if (routeIds.contains(btRoute.mRoute.getId())) {
+ continue;
+ }
+ routes.add(btRoute.mRoute);
+ routeIds.add(btRoute.mRoute.getId());
}
- routes.add(btRoute.mRoute);
- routeIds.add(btRoute.mRoute.getId());
}
return routes;
}
- /**
- * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}.
- *
- * @return true if devices can be handled by the provider.
- */
@Override
public boolean updateVolumeForDevices(int devices, int volume) {
int routeType;
@@ -270,32 +326,31 @@
} else {
return false;
}
- mVolumeMap.put(routeType, volume);
- boolean shouldNotify = false;
- for (BluetoothRouteInfo btRoute : mActiveRoutes) {
- if (btRoute.mRoute.getType() != routeType) {
- continue;
+ synchronized (this) {
+ mVolumeMap.put(routeType, volume);
+ if (mSelectedBluetoothRoute == null
+ || mSelectedBluetoothRoute.mRoute.getType() != routeType) {
+ return false;
}
- btRoute.mRoute = new MediaRoute2Info.Builder(btRoute.mRoute)
- .setVolume(volume)
- .build();
- shouldNotify = true;
+
+ mSelectedBluetoothRoute.mRoute =
+ new MediaRoute2Info.Builder(mSelectedBluetoothRoute.mRoute)
+ .setVolume(volume)
+ .build();
}
- if (shouldNotify) {
- notifyBluetoothRoutesUpdated();
- }
+
+ notifyBluetoothRoutesUpdated();
return true;
}
private void notifyBluetoothRoutesUpdated() {
- if (mListener != null) {
- mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
- }
+ mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
}
private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
- BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
+ BluetoothRouteInfo
+ newBtRoute = new BluetoothRouteInfo();
newBtRoute.mBtDevice = device;
String routeId = device.getAddress();
@@ -305,20 +360,20 @@
}
int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
newBtRoute.mConnectedProfiles = new SparseBooleanArray();
- if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
+ if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.A2DP, device)) {
newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true);
}
- if (mHearingAidProfile != null
- && mHearingAidProfile.getConnectedDevices().contains(device)) {
+ if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.HEARING_AID, device)) {
newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
// Intentionally assign the same ID for a pair of devices to publish only one of them.
- routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
+ routeId = HEARING_AID_ROUTE_ID_PREFIX
+ + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.HEARING_AID, device);
type = MediaRoute2Info.TYPE_HEARING_AID;
}
- if (mLeAudioProfile != null
- && mLeAudioProfile.getConnectedDevices().contains(device)) {
+ if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.LE_AUDIO, device)) {
newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
- routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device);
+ routeId = LE_AUDIO_ROUTE_ID_PREFIX
+ + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.LE_AUDIO, device);
type = MediaRoute2Info.TYPE_BLE_HEADSET;
}
@@ -351,72 +406,19 @@
.setConnectionState(state);
builder.setType(btRoute.getRouteType());
+
+
if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
- builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0));
+ int currentVolume;
+ synchronized (this) {
+ currentVolume = mVolumeMap.get(btRoute.getRouteType(), 0);
+ }
+ builder.setVolume(currentVolume);
}
+
btRoute.mRoute = builder.build();
}
- private void addActiveRoute(BluetoothRouteInfo btRoute) {
- if (btRoute == null) {
- Slog.w(TAG, "addActiveRoute: btRoute is null");
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "Adding active route: " + btRoute.mRoute);
- }
- if (mActiveRoutes.contains(btRoute)) {
- Slog.w(TAG, "addActiveRoute: btRoute is already added.");
- return;
- }
- setRouteConnectionState(btRoute, STATE_CONNECTED);
- mActiveRoutes.add(btRoute);
- }
-
- private void removeActiveRoute(BluetoothRouteInfo btRoute) {
- if (DEBUG) {
- Log.d(TAG, "Removing active route: " + btRoute.mRoute);
- }
- if (mActiveRoutes.remove(btRoute)) {
- setRouteConnectionState(btRoute, STATE_DISCONNECTED);
- }
- }
-
- private void clearActiveRoutesWithType(int type) {
- if (DEBUG) {
- Log.d(TAG, "Clearing active routes with type. type=" + type);
- }
- Iterator<BluetoothRouteInfo> iter = mActiveRoutes.iterator();
- while (iter.hasNext()) {
- BluetoothRouteInfo btRoute = iter.next();
- if (btRoute.mRoute.getType() == type) {
- iter.remove();
- setRouteConnectionState(btRoute, STATE_DISCONNECTED);
- }
- }
- }
-
- private void addActiveDevices(BluetoothDevice device) {
- // Let the given device be the first active device
- BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
- // This could happen if ACTION_ACTIVE_DEVICE_CHANGED is sent before
- // ACTION_CONNECTION_STATE_CHANGED is sent.
- if (activeBtRoute == null) {
- activeBtRoute = createBluetoothRoute(device);
- mBluetoothRoutes.put(device.getAddress(), activeBtRoute);
- }
- addActiveRoute(activeBtRoute);
-
- // A bluetooth route with the same route ID should be added.
- for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
- if (TextUtils.equals(btRoute.mRoute.getId(), activeBtRoute.mRoute.getId())
- && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
- activeBtRoute.mBtDevice.getAddress())) {
- addActiveRoute(btRoute);
- }
- }
- }
-
private static class BluetoothRouteInfo {
private BluetoothDevice mBtDevice;
private MediaRoute2Info mRoute;
@@ -437,71 +439,25 @@
}
}
- // These callbacks run on the main thread.
- private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- List<BluetoothDevice> activeDevices;
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dpProfile = (BluetoothA2dp) proxy;
- // It may contain null.
- activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP);
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAidProfile = (BluetoothHearingAid) proxy;
- activeDevices = mBluetoothAdapter.getActiveDevices(
- BluetoothProfile.HEARING_AID);
- break;
- case BluetoothProfile.LE_AUDIO:
- mLeAudioProfile = (BluetoothLeAudio) proxy;
- activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
- break;
- default:
- return;
- }
- for (BluetoothDevice device : proxy.getConnectedDevices()) {
- BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
- if (btRoute == null) {
- btRoute = createBluetoothRoute(device);
- mBluetoothRoutes.put(device.getAddress(), btRoute);
- }
- if (activeDevices.contains(device)) {
- addActiveRoute(btRoute);
- }
- }
- notifyBluetoothRoutesUpdated();
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dpProfile = null;
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAidProfile = null;
- break;
- case BluetoothProfile.LE_AUDIO:
- mLeAudioProfile = null;
- break;
- default:
- return;
- }
- }
- }
-
private class AdapterStateChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF
|| state == BluetoothAdapter.STATE_TURNING_OFF) {
- mBluetoothRoutes.clear();
+ synchronized (AudioPoliciesBluetoothRouteController.this) {
+ mBluetoothRoutes.clear();
+ }
notifyBluetoothRoutesUpdated();
} else if (state == BluetoothAdapter.STATE_ON) {
- buildBluetoothRoutes();
- if (!mBluetoothRoutes.isEmpty()) {
+ updateBluetoothRoutes();
+
+ boolean shouldCallListener;
+ synchronized (AudioPoliciesBluetoothRouteController.this) {
+ shouldCallListener = !mBluetoothRoutes.isEmpty();
+ }
+
+ if (shouldCallListener) {
notifyBluetoothRoutesUpdated();
}
}
@@ -511,74 +467,15 @@
private class DeviceStateChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- BluetoothDevice device = intent.getParcelableExtra(
- BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);
-
switch (intent.getAction()) {
case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
- clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
- if (device != null) {
- addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
- }
- notifyBluetoothRoutesUpdated();
- break;
case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
- clearActiveRoutesWithType(MediaRoute2Info.TYPE_HEARING_AID);
- if (device != null) {
- if (DEBUG) {
- Log.d(TAG, "Setting active hearing aid devices. device=" + device);
- }
-
- addActiveDevices(device);
- }
- notifyBluetoothRoutesUpdated();
- break;
case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
- clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET);
- if (device != null) {
- if (DEBUG) {
- Log.d(TAG, "Setting active le audio devices. device=" + device);
- }
-
- addActiveDevices(device);
- }
- notifyBluetoothRoutesUpdated();
- break;
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
- handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
- break;
case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
- handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
- break;
case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
- handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device);
- break;
- }
- }
-
- private void handleConnectionStateChanged(int profile, Intent intent,
- BluetoothDevice device) {
- int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
- if (state == BluetoothProfile.STATE_CONNECTED) {
- if (btRoute == null) {
- btRoute = createBluetoothRoute(device);
- if (btRoute.mConnectedProfiles.size() > 0) {
- mBluetoothRoutes.put(device.getAddress(), btRoute);
- notifyBluetoothRoutesUpdated();
- }
- } else {
- btRoute.mConnectedProfiles.put(profile, true);
- }
- } else if (state == BluetoothProfile.STATE_DISCONNECTING
- || state == BluetoothProfile.STATE_DISCONNECTED) {
- if (btRoute != null) {
- btRoute.mConnectedProfiles.delete(profile);
- if (btRoute.mConnectedProfiles.size() == 0) {
- removeActiveRoute(mBluetoothRoutes.remove(device.getAddress()));
- notifyBluetoothRoutesUpdated();
- }
- }
+ updateBluetoothRoutes();
+ notifyBluetoothRoutesUpdated();
}
}
}
diff --git a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
new file mode 100644
index 0000000..b129dd0
--- /dev/null
+++ b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import java.util.Objects;
+
+/* package */ class BluetoothProfileMonitor {
+
+ /* package */ static final long GROUP_ID_NO_GROUP = -1L;
+
+ @NonNull
+ private final ProfileListener mProfileListener = new ProfileListener();
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BluetoothAdapter mBluetoothAdapter;
+
+ @Nullable
+ private BluetoothA2dp mA2dpProfile;
+ @Nullable
+ private BluetoothHearingAid mHearingAidProfile;
+ @Nullable
+ private BluetoothLeAudio mLeAudioProfile;
+
+ @Nullable
+ private OnProfileChangedListener mOnProfileChangedListener;
+
+ BluetoothProfileMonitor(@NonNull Context context,
+ @NonNull BluetoothAdapter bluetoothAdapter) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(bluetoothAdapter);
+
+ mContext = context;
+ mBluetoothAdapter = bluetoothAdapter;
+ }
+
+ /* package */ synchronized void setOnProfileChangedListener(
+ @NonNull OnProfileChangedListener listener) {
+ mOnProfileChangedListener = listener;
+ }
+
+ /* package */ void start() {
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+ mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
+ }
+
+ /* package */ boolean isProfileSupported(int profile, @NonNull BluetoothDevice device) {
+ BluetoothProfile bluetoothProfile;
+
+ synchronized (this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ bluetoothProfile = mA2dpProfile;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ bluetoothProfile = mLeAudioProfile;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ bluetoothProfile = mHearingAidProfile;
+ break;
+ default:
+ throw new IllegalArgumentException(profile
+ + " is not supported as Bluetooth profile");
+ }
+ }
+
+ if (bluetoothProfile == null) {
+ return false;
+ }
+
+ return bluetoothProfile.getConnectedDevices().contains(device);
+ }
+
+ /* package */ long getGroupId(int profile, @NonNull BluetoothDevice device) {
+ synchronized (this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ return GROUP_ID_NO_GROUP;
+ case BluetoothProfile.LE_AUDIO:
+ return mLeAudioProfile == null ? GROUP_ID_NO_GROUP : mLeAudioProfile.getGroupId(
+ device);
+ case BluetoothProfile.HEARING_AID:
+ return mHearingAidProfile == null
+ ? GROUP_ID_NO_GROUP : mHearingAidProfile.getHiSyncId(device);
+ default:
+ throw new IllegalArgumentException(profile
+ + " is not supported as Bluetooth profile");
+ }
+ }
+ }
+
+ /* package */ interface OnProfileChangedListener {
+ void onProfileChange(int profile);
+ }
+
+ private final class ProfileListener implements BluetoothProfile.ServiceListener {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ OnProfileChangedListener listener;
+
+ synchronized (BluetoothProfileMonitor.this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = (BluetoothA2dp) proxy;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = (BluetoothHearingAid) proxy;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = (BluetoothLeAudio) proxy;
+ break;
+ default:
+ return;
+ }
+
+ listener = mOnProfileChangedListener;
+ }
+
+ if (listener != null) {
+ listener.onProfileChange(profile);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ OnProfileChangedListener listener;
+
+ synchronized (BluetoothProfileMonitor.this) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dpProfile = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAidProfile = null;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudioProfile = null;
+ break;
+ default:
+ return;
+ }
+
+ listener = mOnProfileChangedListener;
+ }
+
+ if (listener != null) {
+ listener.onProfileChange(profile);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/MediaFeatureFlagManager.java b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
new file mode 100644
index 0000000..723cda0
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaFeatureFlagManager.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.StringDef;
+import android.provider.DeviceConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* package */ class MediaFeatureFlagManager {
+
+ /**
+ * Namespace for media better together features.
+ */
+ private static final String NAMESPACE_MEDIA_BETTER_TOGETHER = "media_better_together";
+
+ @StringDef(prefix = "FEATURE_", value = {
+ FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER
+ })
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
+ @Retention(RetentionPolicy.SOURCE)
+ /* package */ @interface MediaFeatureFlag {}
+
+ /**
+ * Whether to use old legacy implementation of BluetoothRouteController or new
+ * 'Audio Strategies'-aware controller.
+ */
+ /* package */ static final @MediaFeatureFlag String
+ FEATURE_IS_USING_LEGACY_BLUETOOTH_CONTROLLER =
+ "BluetoothRouteController__enable_legacy_bluetooth_routes_controller";
+
+ private static final MediaFeatureFlagManager sInstance = new MediaFeatureFlagManager();
+
+ private MediaFeatureFlagManager() {
+ // Empty to prevent instantiation.
+ }
+
+ /* package */ MediaFeatureFlagManager getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Returns a boolean value from {@link DeviceConfig} from the system_time namespace, or
+ * {@code defaultValue} if there is no explicit value set.
+ */
+ public boolean getBoolean(@MediaFeatureFlag String key, boolean defaultValue) {
+ return DeviceConfig.getBoolean(NAMESPACE_MEDIA_BETTER_TOGETHER, key, defaultValue);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4b2c88c..2774462 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,7 +20,7 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
-import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index c232b36..19575a3 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -59,7 +59,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -677,5 +676,5 @@
UserInfo[] getUserInfos();
@NonNull
- Collection<SharedUserSetting> getAllSharedUsers();
+ ArrayMap<String, ? extends SharedUserApi> getSharedUsers();
}
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 5984360..14b72ff 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -301,8 +301,8 @@
}
@NonNull
- public Collection<SharedUserSetting> getAllSharedUsers() {
- return mSettings.getAllSharedUsersLPw();
+ ArrayMap<String, ? extends SharedUserApi> getSharedUsers() {
+ return mSettings.getSharedUsersLocked().untrackedStorage();
}
@Nullable
@@ -5484,8 +5484,8 @@
@Override
public SparseArray<String> getAppsWithSharedUserIds() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
- for (SharedUserSetting setting : mSettings.getAllSharedUsers()) {
- sharedUserIds.put(UserHandle.getAppId(setting.mAppId), setting.name);
+ for (SharedUserApi sharedUser : mSettings.getSharedUsers().values()) {
+ sharedUserIds.put(UserHandle.getAppId(sharedUser.getAppId()), sharedUser.getName());
}
return sharedUserIds;
}
@@ -5785,8 +5785,8 @@
@Override
@NonNull
- public Collection<SharedUserSetting> getAllSharedUsers() {
- return mSettings.getAllSharedUsers();
+ public ArrayMap<String, ? extends SharedUserApi> getSharedUsers() {
+ return mSettings.getSharedUsers();
}
@Override
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index a868470b..7fe6c7d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -164,6 +164,7 @@
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.art.model.DexoptParams;
+import com.android.server.art.model.DexoptResult;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexManager;
@@ -2534,8 +2535,9 @@
packageManagerLocal.withFilteredSnapshot()) {
DexoptParams params =
dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
- DexOptHelper.getArtManagerLocal().dexoptPackage(
+ DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
+ installRequest.onDexoptFinished(dexOptResult);
}
} else {
try {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 46ea010..95e7904 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -43,6 +43,7 @@
import android.util.ExceptionUtils;
import android.util.Slog;
+import com.android.server.art.model.DexoptResult;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
@@ -127,6 +128,8 @@
private final int mSessionId;
private final int mRequireUserAction;
+ private int mDexoptStatus;
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -609,6 +612,10 @@
return mRequireUserAction;
}
+ public int getDexoptStatus() {
+ return mDexoptStatus;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
@@ -799,6 +806,25 @@
}
}
+ public void onDexoptFinished(DexoptResult dexoptResult) {
+ if (mPackageMetrics == null) {
+ return;
+ }
+ mDexoptStatus = dexoptResult.getFinalStatus();
+ if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
+ return;
+ }
+ long durationMillis = 0;
+ for (DexoptResult.PackageDexoptResult packageResult :
+ dexoptResult.getPackageDexoptResults()) {
+ for (DexoptResult.DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ durationMillis += fileResult.getDex2oatWallTimeMillis();
+ }
+ }
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+ }
+
public void onInstallCompleted() {
if (getReturnCode() == INSTALL_SUCCEEDED) {
if (mPackageMetrics != null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerLocal.java b/services/core/java/com/android/server/pm/PackageManagerLocal.java
index 935c4dd..6266ef3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerLocal.java
+++ b/services/core/java/com/android/server/pm/PackageManagerLocal.java
@@ -24,6 +24,7 @@
import android.os.UserHandle;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedUserApi;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -150,6 +151,16 @@
Map<String, PackageState> getPackageStates();
/**
+ * Returns a map of all {@link SharedUserApi SharedUsers} on the device.
+ *
+ * @return Mapping of shared user name to {@link SharedUserApi}.
+ *
+ * @hide Pending API
+ */
+ @NonNull
+ Map<String, SharedUserApi> getSharedUsers();
+
+ /**
* Returns a map of all disabled system {@link PackageState PackageStates} on the device.
*
* @return Mapping of package name to disabled system {@link PackageState}.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index de5f0c4..b157781 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -218,6 +218,7 @@
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
+import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -6746,6 +6747,13 @@
}
@Override
+ public LegacyPermissionSettings getLegacyPermissions() {
+ synchronized (mLock) {
+ return mSettings.mPermissions;
+ }
+ }
+
+ @Override
@SuppressWarnings("GuardedBy")
public boolean isPermissionUpgradeNeeded(int userId) {
return mSettings.isPermissionUpgradeNeeded(userId);
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index fe014a4..80d6ebb 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -48,12 +48,14 @@
public static final int STEP_SCAN = 2;
public static final int STEP_RECONCILE = 3;
public static final int STEP_COMMIT = 4;
+ public static final int STEP_DEXOPT = 5;
@IntDef(prefix = {"STEP_"}, value = {
STEP_PREPARE,
STEP_SCAN,
STEP_RECONCILE,
STEP_COMMIT,
+ STEP_DEXOPT
})
@Retention(RetentionPolicy.SOURCE)
public @interface StepInt {
@@ -175,6 +177,10 @@
}
}
+ public void onStepFinished(@StepInt int step, long durationMillis) {
+ mInstallSteps.put(step, new InstallStep(durationMillis));
+ }
+
// List of steps (e.g., 1, 2, 3) and corresponding list of durations (e.g., 200ms, 100ms, 150ms)
private Pair<int[], long[]> getInstallStepDurations() {
ArrayList<Integer> steps = new ArrayList<>();
@@ -203,6 +209,11 @@
mStartTimestampMillis = System.currentTimeMillis();
}
+ InstallStep(long durationMillis) {
+ mStartTimestampMillis = -1;
+ mDurationMillis = durationMillis;
+ }
+
void finish() {
mDurationMillis = System.currentTimeMillis() - mStartTimestampMillis;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f1998f7..70ca17f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -110,6 +110,7 @@
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.component.ParsedIntentInfo;
@@ -868,6 +869,10 @@
return s;
}
+ WatchedArrayMap<String, ? extends SharedUserApi> getSharedUsersLocked() {
+ return mSharedUsers;
+ }
+
Collection<SharedUserSetting> getAllSharedUsersLPw() {
return mSharedUsers.values();
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 9ef1bba..b4d467f 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -64,12 +64,11 @@
})
public @interface UserAssignmentResult {}
- // TODO(b/248408342): Move keep annotation to the method referencing these fields reflectively.
- @Keep public static final int USER_START_MODE_FOREGROUND = 1;
- @Keep public static final int USER_START_MODE_BACKGROUND = 2;
- @Keep public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;
-
private static final String PREFIX_USER_START_MODE = "USER_START_MODE_";
+
+ /**
+ * Type used to indicate how a user started.
+ */
@IntDef(flag = false, prefix = {PREFIX_USER_START_MODE}, value = {
USER_START_MODE_FOREGROUND,
USER_START_MODE_BACKGROUND,
@@ -77,6 +76,32 @@
})
public @interface UserStartMode {}
+ // TODO(b/248408342): Move keep annotations below to the method referencing these fields
+ // reflectively.
+
+ /** (Full) user started on foreground (a.k.a. "current user"). */
+ @Keep public static final int USER_START_MODE_FOREGROUND = 1;
+
+ /**
+ * User (full or profile) started on background and is
+ * {@link UserManager#isUserVisible() invisible}.
+ *
+ * <p>This is the "traditional" way of starting a background user, and can be used to start
+ * profiles as well, although starting an invisible profile is not common from the System UI
+ * (it could be done through APIs or adb, though).
+ */
+ @Keep public static final int USER_START_MODE_BACKGROUND = 2;
+
+ /**
+ * User (full or profile) started on background and is
+ * {@link UserManager#isUserVisible() visible}.
+ *
+ * <p>This is the "traditional" way of starting a profile (i.e., when the profile of the current
+ * user is the current foreground user), but it can also be used to start a full user associated
+ * with a display (which is the case on automotives with passenger displays).
+ */
+ @Keep public static final int USER_START_MODE_BACKGROUND_VISIBLE = 3;
+
public interface UserRestrictionsListener {
/**
* Called when a user restriction changes.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dd014ee..ab826cb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -114,6 +114,7 @@
import android.util.TypedValue;
import android.util.Xml;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
@@ -2391,41 +2392,58 @@
@Override
public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
checkCreateUsersPermission("update ephemeral user flag");
- UserData userToUpdate = null;
+ return enableEphemeral
+ ? UserManager.isRemoveResultSuccessful(setUserEphemeralUnchecked(userId))
+ : setUserNonEphemeralUnchecked(userId);
+ }
+
+ private boolean setUserNonEphemeralUnchecked(@UserIdInt int userId) {
synchronized (mPackagesLock) {
+ final UserData userData;
synchronized (mUsersLock) {
- final UserData userData = mUsers.get(userId);
+ userData = mUsers.get(userId);
if (userData == null) {
- Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId);
+ Slog.e(LOG_TAG, TextUtils.formatSimple(
+ "Cannot set user %d non-ephemeral, invalid user id provided.", userId));
return false;
}
- boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0;
- boolean isEphemeralOnCreateUser =
- (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0;
- // when user is created in ephemeral mode via FLAG_EPHEMERAL
- // its state cannot be changed to non ephemeral.
- // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state
- if (isEphemeralOnCreateUser && !enableEphemeral) {
- Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user "
- + userId);
+ if (!userData.info.isEphemeral()) {
+ return true;
+ }
+
+ if ((userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0) {
+ // when user is created in ephemeral mode via FLAG_EPHEMERAL
+ // its state cannot be changed to non-ephemeral.
+ // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state
+ Slog.e(LOG_TAG, TextUtils.formatSimple("User %d can not be changed to "
+ + "non-ephemeral because it was set ephemeral on create.", userId));
return false;
}
- if (isEphemeralUser != enableEphemeral) {
- if (enableEphemeral) {
- userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
- } else {
- userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL;
- }
- userToUpdate = userData;
- }
}
- if (userToUpdate != null) {
- writeUserLP(userToUpdate);
- }
+ userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL;
+ writeUserLP(userData);
}
return true;
}
+ private @UserManager.RemoveResult int setUserEphemeralUnchecked(@UserIdInt int userId) {
+ synchronized (mPackagesLock) {
+ final UserData userData;
+ synchronized (mUsersLock) {
+ final int userRemovability = getUserRemovabilityLocked(userId, "set as ephemeral");
+ if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) {
+ return userRemovability;
+ }
+ userData = mUsers.get(userId);
+ }
+ userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+ writeUserLP(userData);
+ }
+ Slog.i(LOG_TAG, TextUtils.formatSimple(
+ "User %d is set ephemeral and will be removed on user switch or reboot.", userId));
+ return UserManager.REMOVE_RESULT_DEFERRED;
+ }
+
@Override
public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
try {
@@ -5369,23 +5387,37 @@
}
private boolean removeUserWithProfilesUnchecked(@UserIdInt int userId) {
- UserInfo userInfo = getUserInfoNoChecks(userId);
-
- if (userInfo == null) {
- Slog.e(LOG_TAG, TextUtils.formatSimple(
- "Cannot remove user %d, invalid user id provided.", userId));
- return false;
+ final UserData userData;
+ final boolean isProfile;
+ final IntArray profileIds;
+ synchronized (mUsersLock) {
+ final int userRemovability = getUserRemovabilityLocked(userId, "removed");
+ if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) {
+ return UserManager.isRemoveResultSuccessful(userRemovability);
+ }
+ userData = mUsers.get(userId);
+ isProfile = userData.info.isProfile();
+ profileIds = isProfile ? null : getProfileIdsLU(userId, null, false);
}
- if (!userInfo.isProfile()) {
- int[] profileIds = getProfileIds(userId, false);
- for (int profileId : profileIds) {
+ if (!isProfile) {
+ Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds();
+ if (userId == currentAndTargetUserIds.first) {
+ Slog.w(LOG_TAG, "Current user cannot be removed.");
+ return false;
+ }
+ if (userId == currentAndTargetUserIds.second) {
+ Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed.");
+ return false;
+ }
+ for (int i = profileIds.size() - 1; i >= 0; i--) {
+ int profileId = profileIds.get(i);
if (profileId == userId) {
//Remove the associated profiles first and then remove the user
continue;
}
Slog.i(LOG_TAG, "removing profile:" + profileId
- + "associated with user:" + userId);
+ + " associated with user:" + userId);
if (!removeUserUnchecked(profileId)) {
// If the profile was not immediately removed, make sure it is marked as
// ephemeral. Don't mark as disabled since, per UserInfo.FLAG_DISABLED
@@ -5432,45 +5464,16 @@
final long ident = Binder.clearCallingIdentity();
try {
final UserData userData;
- Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds();
- if (userId == currentAndTargetUserIds.first) {
- Slog.w(LOG_TAG, "Current user cannot be removed.");
- return false;
- }
- if (userId == currentAndTargetUserIds.second) {
- Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed.");
- return false;
- }
synchronized (mPackagesLock) {
synchronized (mUsersLock) {
+ final int userRemovability = getUserRemovabilityLocked(userId, "removed");
+ if (userRemovability != UserManager.REMOVE_RESULT_USER_IS_REMOVABLE) {
+ return UserManager.isRemoveResultSuccessful(userRemovability);
+ }
userData = mUsers.get(userId);
- if (userId == UserHandle.USER_SYSTEM) {
- Slog.e(LOG_TAG, "System user cannot be removed.");
- return false;
- }
-
- if (userData == null) {
- Slog.e(LOG_TAG, TextUtils.formatSimple(
- "Cannot remove user %d, invalid user id provided.", userId));
- return false;
- }
-
- if (isNonRemovableMainUser(userData.info)) {
- Slog.e(LOG_TAG, "Main user cannot be removed when "
- + "it's a permanent admin user.");
- return false;
- }
-
- if (mRemovingUserIds.get(userId)) {
- Slog.e(LOG_TAG, TextUtils.formatSimple(
- "User %d is already scheduled for removal.", userId));
- return false;
- }
-
Slog.i(LOG_TAG, "Removing user " + userId);
addRemovingUserIdLocked(userId);
}
-
// Set this to a partially created user, so that the user will be purged
// on next startup, in case the runtime stops now before stopping and
// removing the user completely.
@@ -5539,6 +5542,7 @@
@Override
public @UserManager.RemoveResult int removeUserWhenPossible(@UserIdInt int userId,
boolean overrideDevicePolicy) {
+ Slog.i(LOG_TAG, "removeUserWhenPossible u" + userId);
checkCreateUsersPermission("Only the system can remove users");
if (!overrideDevicePolicy) {
@@ -5548,65 +5552,47 @@
return UserManager.REMOVE_RESULT_ERROR_USER_RESTRICTION;
}
}
+ Slog.i(LOG_TAG, "Attempting to immediately remove user " + userId);
+ if (removeUserWithProfilesUnchecked(userId)) {
+ return UserManager.REMOVE_RESULT_REMOVED;
+ }
+ Slog.i(LOG_TAG, TextUtils.formatSimple(
+ "Unable to immediately remove user %d. Now trying to set it ephemeral.", userId));
+ return setUserEphemeralUnchecked(userId);
+ }
+
+ /**
+ * Returns the user's removability status.
+ * User is removable if the return value is {@link UserManager#REMOVE_RESULT_USER_IS_REMOVABLE}.
+ * If the user is not removable this method also prints the reason.
+ * See also {@link UserManager#isRemoveResultSuccessful}.
+ */
+ @GuardedBy("mUsersLock")
+ private @UserManager.RemoveResult int getUserRemovabilityLocked(@UserIdInt int userId,
+ String msg) {
+ String prefix = TextUtils.formatSimple("User %d can not be %s, ", userId, msg);
if (userId == UserHandle.USER_SYSTEM) {
- Slog.e(LOG_TAG, "System user cannot be removed.");
+ Slog.e(LOG_TAG, prefix + "system user cannot be removed.");
return UserManager.REMOVE_RESULT_ERROR_SYSTEM_USER;
}
-
- final long ident = Binder.clearCallingIdentity();
- try {
- final UserData userData;
- synchronized (mPackagesLock) {
- synchronized (mUsersLock) {
- userData = mUsers.get(userId);
- if (userData == null) {
- Slog.e(LOG_TAG,
- "Cannot remove user " + userId + ", invalid user id provided.");
- return UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND;
- }
-
- if (isNonRemovableMainUser(userData.info)) {
- Slog.e(LOG_TAG, "Main user cannot be removed when "
- + "it's a permanent admin user.");
- return UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN;
- }
-
- if (mRemovingUserIds.get(userId)) {
- Slog.e(LOG_TAG, "User " + userId + " is already scheduled for removal.");
- return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED;
- }
- }
-
- // Attempt to immediately remove a non-current and non-target user
- Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds();
- if (userId != currentAndTargetUserIds.first
- && userId != currentAndTargetUserIds.second) {
- // Attempt to remove the user. This will fail if the user is the current user
- if (removeUserWithProfilesUnchecked(userId)) {
- return UserManager.REMOVE_RESULT_REMOVED;
- }
- }
- // If the user was not immediately removed, make sure it is marked as ephemeral.
- // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an
- // ephemeral user should only be marked as disabled when its removal is in progress.
- Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d "
- + "(%s is %d). User is set as ephemeral and will be removed on "
- + "user switch or reboot.",
- userId,
- userId == currentAndTargetUserIds.first
- ? "current user"
- : "target user of an ongoing user switch",
- userId));
- userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
- writeUserLP(userData);
-
- return UserManager.REMOVE_RESULT_DEFERRED;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
+ final UserData userData = mUsers.get(userId);
+ if (userData == null) {
+ Slog.e(LOG_TAG, prefix + "invalid user id provided.");
+ return UserManager.REMOVE_RESULT_ERROR_USER_NOT_FOUND;
}
+ if (isNonRemovableMainUser(userData.info)) {
+ Slog.e(LOG_TAG, prefix
+ + "main user cannot be removed when it's a permanent admin user.");
+ return UserManager.REMOVE_RESULT_ERROR_MAIN_USER_PERMANENT_ADMIN;
+ }
+ if (mRemovingUserIds.get(userId)) {
+ Slog.w(LOG_TAG, prefix + "it is already scheduled for removal.");
+ return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED;
+ }
+ return UserManager.REMOVE_RESULT_USER_IS_REMOVABLE;
}
+
private void finishRemoveUser(final @UserIdInt int userId) {
Slog.i(LOG_TAG, "finishRemoveUser " + userId);
@@ -7369,9 +7355,19 @@
* If the main user is a permanent admin user it can't be deleted
* or downgraded to non-admin status.
*/
- private static boolean isMainUserPermanentAdmin() {
+ public boolean isMainUserPermanentAdmin() {
return Resources.getSystem()
- .getBoolean(com.android.internal.R.bool.config_isMainUserPermanentAdmin);
+ .getBoolean(R.bool.config_isMainUserPermanentAdmin);
+ }
+
+ /**
+ * Returns true if {@link com.android.internal.R.bool#config_canSwitchToHeadlessSystemUser}
+ * is true. If allowed, headless system user can run in the foreground even though
+ * it is not a full user.
+ */
+ public boolean canSwitchToHeadlessSystemUser() {
+ return Resources.getSystem()
+ .getBoolean(R.bool.config_canSwitchToHeadlessSystemUser);
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index eed2a78..98b24ea 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -150,6 +150,10 @@
return runIsUserVisible();
case "get-main-user":
return runGetMainUserId();
+ case "can-switch-to-headless-system-user":
+ return canSwitchToHeadlessSystemUser();
+ case "is-main-user-permanent-admin":
+ return isMainUserPermanentAdmin();
default:
return handleDefaultCommands(cmd);
}
@@ -532,6 +536,20 @@
return 0;
}
+ private int canSwitchToHeadlessSystemUser() {
+ PrintWriter pw = getOutPrintWriter();
+ boolean canSwitchToHeadlessSystemUser = mService.canSwitchToHeadlessSystemUser();
+ pw.println(canSwitchToHeadlessSystemUser);
+ return 0;
+ }
+
+ private int isMainUserPermanentAdmin() {
+ PrintWriter pw = getOutPrintWriter();
+ boolean isMainUserPermanentAdmin = mService.isMainUserPermanentAdmin();
+ pw.println(isMainUserPermanentAdmin);
+ return 0;
+ }
+
/**
* Gets the {@link UserManager} associated with the context of the given user.
*/
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 3f7502b..f87f50a 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -42,6 +42,7 @@
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
+import android.util.Log;
import android.util.SparseIntArray;
import android.view.Display;
@@ -55,6 +56,8 @@
import com.android.server.utils.Slogf;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -77,11 +80,11 @@
*/
public final class UserVisibilityMediator implements Dumpable {
- private static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
- private static final boolean VERBOSE = false; // DO NOT SUBMIT WITH TRUE
-
private static final String TAG = UserVisibilityMediator.class.getSimpleName();
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VERBOSE = false; // DO NOT SUBMIT WITH TRUE
+
private static final String PREFIX_SECONDARY_DISPLAY_MAPPING = "SECONDARY_DISPLAY_MAPPING_";
public static final int SECONDARY_DISPLAY_MAPPING_NEEDED = 1;
public static final int SECONDARY_DISPLAY_MAPPING_NOT_NEEDED = 2;
@@ -98,7 +101,7 @@
})
public @interface SecondaryDisplayMappingStatus {}
- // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
+ // TODO(b/266158156): might need to change this if boot logic is refactored for HSUM devices
@VisibleForTesting
static final int INITIAL_CURRENT_USER_ID = USER_SYSTEM;
@@ -132,10 +135,23 @@
private final SparseIntArray mExtraDisplaysAssignedToUsers;
/**
- * Mapping from each started user to its profile group.
+ * Mapping of each user that started visible (key) to its profile group id (value).
+ *
+ * <p>It's used to determine not just if the user is visible, but also
+ * {@link #isProfile(int, int) if it's a profile}.
*/
@GuardedBy("mLock")
- private final SparseIntArray mStartedProfileGroupIds = new SparseIntArray();
+ private final SparseIntArray mStartedVisibleProfileGroupIds = new SparseIntArray();
+
+ /**
+ * List of profiles that have explicitly started invisible.
+ *
+ * <p>Only used for debugging purposes (and set when {@link #DBG} is {@code true}), hence we
+ * don't care about autoboxing.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private final List<Integer> mStartedInvisibleProfileUserIds;
/**
* Handler user to call listeners
@@ -164,9 +180,14 @@
mUsersAssignedToDisplayOnStart = null;
mExtraDisplaysAssignedToUsers = null;
}
+ mStartedInvisibleProfileUserIds = DBG ? new ArrayList<>(4) : null;
mHandler = handler;
- // TODO(b/242195409): might need to change this if boot logic is refactored for HSUM devices
- mStartedProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID);
+ // TODO(b/266158156): might need to change this if boot logic is refactored for HSUM devices
+ mStartedVisibleProfileGroupIds.put(INITIAL_CURRENT_USER_ID, INITIAL_CURRENT_USER_ID);
+
+ if (DBG) {
+ Slogf.i(TAG, "UserVisibilityMediator created with DBG on");
+ }
}
/**
@@ -177,6 +198,8 @@
int displayId) {
Preconditions.checkArgument(!isSpecialUserId(userId), "user id cannot be generic: %d",
userId);
+ validateUserStartMode(userStartMode);
+
// This method needs to perform 4 actions:
//
// 1. Check if the user can be started given the provided arguments
@@ -224,14 +247,29 @@
visibleUsersBefore = getVisibleUsers();
- // Set current user / profiles state
- if (userStartMode == USER_START_MODE_FOREGROUND) {
- mCurrentUserId = userId;
+ // Set current user / started users state
+ switch (userStartMode) {
+ case USER_START_MODE_FOREGROUND:
+ mCurrentUserId = userId;
+ // Fallthrough
+ case USER_START_MODE_BACKGROUND_VISIBLE:
+ if (DBG) {
+ Slogf.d(TAG, "adding visible user / profile group id mapping (%d -> %d)",
+ userId, profileGroupId);
+ }
+ mStartedVisibleProfileGroupIds.put(userId, profileGroupId);
+ break;
+ case USER_START_MODE_BACKGROUND:
+ if (mStartedInvisibleProfileUserIds != null
+ && isProfile(userId, profileGroupId)) {
+ Slogf.d(TAG, "adding user %d to list of invisible profiles", userId);
+ mStartedInvisibleProfileUserIds.add(userId);
+ }
+ break;
+ default:
+ Slogf.wtf(TAG, "invalid userStartMode passed to assignUserToDisplayOnStart: "
+ + "%d", userStartMode);
}
- if (DBG) {
- Slogf.d(TAG, "adding user / profile mapping (%d -> %d)", userId, profileGroupId);
- }
- mStartedProfileGroupIds.put(userId, profileGroupId);
// Set user / display state
switch (mappingResult) {
@@ -297,38 +335,44 @@
boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
if (displayId != DEFAULT_DISPLAY) {
if (foreground) {
- Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %b, %d) failed: cannot start "
+ Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: cannot start "
+ "foreground user on secondary display", userId, profileGroupId,
- foreground, displayId);
+ userStartModeToString(userStartMode), displayId);
return USER_ASSIGNMENT_RESULT_FAILURE;
}
if (!mVisibleBackgroundUsersEnabled) {
- Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %b, %d) failed: called on "
+ Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: called on "
+ "device that doesn't support multiple users on multiple displays",
- userId, profileGroupId, foreground, displayId);
+ userId, profileGroupId, userStartModeToString(userStartMode), displayId);
return USER_ASSIGNMENT_RESULT_FAILURE;
}
}
if (isProfile(userId, profileGroupId)) {
if (displayId != DEFAULT_DISPLAY) {
- Slogf.w(TAG, "canStartUserLocked(%d, %d, %b, %d) failed: cannot start profile user "
- + "on secondary display", userId, profileGroupId, foreground,
- displayId);
+ Slogf.w(TAG, "canStartUserLocked(%d, %d, %s, %d) failed: cannot start profile user "
+ + "on secondary display", userId, profileGroupId,
+ userStartModeToString(userStartMode), displayId);
return USER_ASSIGNMENT_RESULT_FAILURE;
}
- if (foreground) {
- Slogf.w(TAG, "startUser(%d, %d, %b, %d) failed: cannot start profile user in "
- + "foreground", userId, profileGroupId, foreground, displayId);
- return USER_ASSIGNMENT_RESULT_FAILURE;
- } else {
- boolean isParentVisibleOnDisplay = isUserVisible(profileGroupId, displayId);
- if (DBG) {
- Slogf.d(TAG, "parent visible on display: %b", isParentVisibleOnDisplay);
- }
- return isParentVisibleOnDisplay
- ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
- : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
+ switch (userStartMode) {
+ case USER_START_MODE_FOREGROUND:
+ Slogf.w(TAG, "startUser(%d, %d, %s, %d) failed: cannot start profile user in "
+ + "foreground", userId, profileGroupId,
+ userStartModeToString(userStartMode), displayId);
+ return USER_ASSIGNMENT_RESULT_FAILURE;
+ case USER_START_MODE_BACKGROUND_VISIBLE:
+ boolean isParentVisibleOnDisplay = isUserVisible(profileGroupId, displayId);
+ if (!isParentVisibleOnDisplay) {
+ Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %s, %d) failed: cannot"
+ + " start profile user visible when its parent is not visible in "
+ + "that display", userId, profileGroupId,
+ userStartModeToString(userStartMode), displayId);
+ return USER_ASSIGNMENT_RESULT_FAILURE;
+ }
+ return USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
+ case USER_START_MODE_BACKGROUND:
+ return USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
}
} else if (mUsersAssignedToDisplayOnStart != null
&& isUserAssignedToDisplayOnStartLocked(userId, displayId)) {
@@ -353,8 +397,9 @@
if (mVisibleBackgroundUserOnDefaultDisplayAllowed
&& userStartMode == USER_START_MODE_BACKGROUND_VISIBLE) {
int userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
- if (userStartedOnDefaultDisplay != USER_NULL) {
- Slogf.w(TAG, "getUserVisibilityOnStartLocked(): cannot start user %d visible on"
+ if (userStartedOnDefaultDisplay != USER_NULL
+ && userStartedOnDefaultDisplay != profileGroupId) {
+ Slogf.w(TAG, "canAssignUserToDisplayLocked(): cannot start user %d visible on"
+ " default display because user %d already did so", userId,
userStartedOnDefaultDisplay);
return SECONDARY_DISPLAY_MAPPING_FAILED;
@@ -468,7 +513,7 @@
userId, displayId);
return false;
}
- if (isStartedProfile(userId)) {
+ if (isStartedVisibleProfileLocked(userId)) {
Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user is a profile",
userId, displayId);
return false;
@@ -556,10 +601,14 @@
@GuardedBy("mLock")
private void unassignUserFromAllDisplaysOnStopLocked(@UserIdInt int userId) {
if (DBG) {
- Slogf.d(TAG, "Removing %d from mStartedProfileGroupIds (%s)", userId,
- mStartedProfileGroupIds);
+ Slogf.d(TAG, "Removing %d from mStartedVisibleProfileGroupIds (%s)", userId,
+ mStartedVisibleProfileGroupIds);
}
- mStartedProfileGroupIds.delete(userId);
+ mStartedVisibleProfileGroupIds.delete(userId);
+ if (mStartedInvisibleProfileUserIds != null) {
+ Slogf.d(TAG, "Removing %d from list of invisible profiles", userId);
+ mStartedInvisibleProfileUserIds.remove(Integer.valueOf(userId));
+ }
if (!mVisibleBackgroundUsersEnabled) {
// Don't need to update mUsersAssignedToDisplayOnStart because methods (such as
@@ -589,7 +638,8 @@
* See {@link UserManagerInternal#isUserVisible(int)}.
*/
public boolean isUserVisible(@UserIdInt int userId) {
- // First check current foreground user and their profiles (on main display)
+ // For optimization (as most devices don't support visible background users), check for
+ // current foreground user and their profiles first
if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
if (VERBOSE) {
Slogf.v(TAG, "isUserVisible(%d): true to current user or profile", userId);
@@ -598,19 +648,31 @@
}
if (!mVisibleBackgroundUsersEnabled) {
- if (DBG) {
- Slogf.d(TAG, "isUserVisible(%d): false for non-current user (or its profiles) when"
+ if (VERBOSE) {
+ Slogf.v(TAG, "isUserVisible(%d): false for non-current user (or its profiles) when"
+ " device doesn't support visible background users", userId);
}
return false;
}
- boolean visible;
+
synchronized (mLock) {
- visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0;
+ int profileGroupId;
+ synchronized (mLock) {
+ profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
+ }
+ if (isProfile(userId, profileGroupId)) {
+ return isUserAssignedToDisplayOnStartLocked(profileGroupId);
+ }
+ return isUserAssignedToDisplayOnStartLocked(userId);
}
- if (DBG) {
- Slogf.d(TAG, "isUserVisible(%d): %b from mapping", userId, visible);
+ }
+
+ @GuardedBy("mLock")
+ private boolean isUserAssignedToDisplayOnStartLocked(@UserIdInt int userId) {
+ boolean visible = mUsersAssignedToDisplayOnStart.indexOfKey(userId) >= 0;
+ if (VERBOSE) {
+ Slogf.v(TAG, "isUserAssignedToDisplayOnStartLocked(%d): %b", userId, visible);
}
return visible;
}
@@ -640,7 +702,8 @@
return false;
}
- // Current user is always visible on:
+ // For optimization (as most devices don't support visible background users), check for
+ // current user and profile first. Current user is always visible on:
// - Default display
// - Secondary displays when device doesn't support visible bg users
// - Or when explicitly added (which is checked below)
@@ -662,16 +725,28 @@
}
synchronized (mLock) {
- if (mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY) == displayId) {
- // User assigned to display on start
- return true;
+ int profileGroupId;
+ synchronized (mLock) {
+ profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
}
-
- // Check for extra display assignment
- return mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId;
+ if (isProfile(userId, profileGroupId)) {
+ return isFullUserVisibleOnBackgroundLocked(profileGroupId, displayId);
+ }
+ return isFullUserVisibleOnBackgroundLocked(userId, displayId);
}
}
+ // NOTE: it doesn't check if the userId is a full user, it's up to the caller to check that
+ @GuardedBy("mLock")
+ private boolean isFullUserVisibleOnBackgroundLocked(@UserIdInt int userId, int displayId) {
+ if (mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY) == displayId) {
+ // User assigned to display on start
+ return true;
+ }
+ // Check for extra display assignment
+ return mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId;
+ }
+
/**
* See {@link UserManagerInternal#getDisplayAssignedToUser(int)}.
*/
@@ -737,7 +812,7 @@
continue;
}
int userId = mUsersAssignedToDisplayOnStart.keyAt(i);
- if (!isStartedProfile(userId)) {
+ if (!isStartedVisibleProfileLocked(userId)) {
return userId;
} else if (DBG) {
Slogf.d(TAG, "getUserAssignedToDisplay(%d): skipping user %d because it's "
@@ -770,8 +845,8 @@
// number of users is too small, the gain is probably not worth the increase on complexity.
IntArray visibleUsers = new IntArray();
synchronized (mLock) {
- for (int i = 0; i < mStartedProfileGroupIds.size(); i++) {
- int userId = mStartedProfileGroupIds.keyAt(i);
+ for (int i = 0; i < mStartedVisibleProfileGroupIds.size(); i++) {
+ int userId = mStartedVisibleProfileGroupIds.keyAt(i);
if (isUserVisible(userId)) {
visibleUsers.add(userId);
}
@@ -804,7 +879,7 @@
}
}
- // TODO(b/242195409): remove this method if not needed anymore
+ // TODO(b/266158156): remove this method if not needed anymore
/**
* Nofify all listeners that the system user visibility changed.
*/
@@ -866,6 +941,9 @@
ipw.println("UserVisibilityMediator");
ipw.increaseIndent();
+ ipw.print("DBG: ");
+ ipw.println(DBG);
+
synchronized (mLock) {
ipw.print("Current user id: ");
ipw.println(mCurrentUserId);
@@ -873,8 +951,12 @@
ipw.print("Visible users: ");
ipw.println(getVisibleUsers());
- dumpSparseIntArray(ipw, mStartedProfileGroupIds, "started user / profile group",
- "u", "pg");
+ dumpSparseIntArray(ipw, mStartedVisibleProfileGroupIds,
+ "started visible user / profile group", "u", "pg");
+ if (mStartedInvisibleProfileUserIds != null) {
+ ipw.print("Profiles started invisible: ");
+ ipw.println(mStartedInvisibleProfileUserIds);
+ }
ipw.print("Supports visible background users on displays: ");
ipw.println(mVisibleBackgroundUsersEnabled);
@@ -982,22 +1064,25 @@
if (mCurrentUserId == userId) {
return true;
}
- return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID) == mCurrentUserId;
+ return mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID)
+ == mCurrentUserId;
}
}
- private boolean isStartedProfile(@UserIdInt int userId) {
- int profileGroupId;
- synchronized (mLock) {
- profileGroupId = mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
- }
+ @GuardedBy("mLock")
+ private boolean isStartedVisibleProfileLocked(@UserIdInt int userId) {
+ int profileGroupId = mStartedVisibleProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
return isProfile(userId, profileGroupId);
}
- private @UserIdInt int getStartedProfileGroupId(@UserIdInt int userId) {
- synchronized (mLock) {
- return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
+ private void validateUserStartMode(@UserStartMode int userStartMode) {
+ switch (userStartMode) {
+ case USER_START_MODE_FOREGROUND:
+ case USER_START_MODE_BACKGROUND:
+ case USER_START_MODE_BACKGROUND_VISIBLE:
+ return;
}
+ throw new IllegalArgumentException("Invalid user start mode: " + userStartMode);
}
private static String secondaryDisplayMappingStatusToString(
diff --git a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
index 4e0a11d..8d05450 100644
--- a/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
+++ b/services/core/java/com/android/server/pm/local/PackageManagerLocalImpl.java
@@ -28,6 +28,7 @@
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import java.io.IOException;
@@ -105,6 +106,9 @@
private Map<String, PackageState> mCachedUnmodifiablePackageStates;
@Nullable
+ private Map<String, SharedUserApi> mCachedUnmodifiableSharedUsers;
+
+ @Nullable
private Map<String, PackageState> mCachedUnmodifiableDisabledSystemPackageStates;
private UnfilteredSnapshotImpl(@NonNull PackageDataSnapshot snapshot) {
@@ -132,6 +136,19 @@
@SuppressWarnings("RedundantSuppression")
@NonNull
@Override
+ public Map<String, SharedUserApi> getSharedUsers() {
+ checkClosed();
+
+ if (mCachedUnmodifiableSharedUsers == null) {
+ mCachedUnmodifiableSharedUsers =
+ Collections.unmodifiableMap(mSnapshot.getSharedUsers());
+ }
+ return mCachedUnmodifiableSharedUsers;
+ }
+
+ @SuppressWarnings("RedundantSuppression")
+ @NonNull
+ @Override
public Map<String, PackageState> getDisabledSystemPackageStates() {
checkClosed();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelper.java b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelper.java
new file mode 100644
index 0000000..e91ddfd
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelper.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.content.pm.PermissionInfo;
+
+import java.util.Map;
+
+/**
+ * In-process api for permissions migration.
+ *
+ * @hide
+ */
+public interface PermissionMigrationHelper {
+ /**
+ * @return legacy permission definitions.
+ */
+ @NonNull
+ Map<String, LegacyPermission> getLegacyPermissions();
+
+ /**
+ * @return legacy permission trees.
+ */
+ @NonNull
+ Map<String, LegacyPermission> getLegacyPermissionTrees();
+
+ /**
+ * @return legacy permissions state for a user.
+ */
+ @NonNull
+ Map<Integer, Map<String, LegacyPermissionState>> getLegacyPermissionStates(int userId);
+
+ /**
+ * Legacy permission definition.
+ */
+ final class LegacyPermission {
+ private final PermissionInfo mPermissionInfo;
+ private final int mType;
+
+ LegacyPermission(PermissionInfo permissionInfo, int type) {
+ mPermissionInfo = permissionInfo;
+ mType = type;
+ }
+
+ @NonNull
+ public PermissionInfo getPermissionInfo() {
+ return mPermissionInfo;
+ }
+
+ public int getType() {
+ return mType;
+ }
+ }
+
+ /**
+ * State of a legacy permission.
+ */
+ final class LegacyPermissionState {
+ private final boolean mGranted;
+ private final int mFlags;
+
+ LegacyPermissionState(boolean granted, int flags) {
+ mGranted = granted;
+ mFlags = flags;
+ }
+
+ /**
+ * @return Whether the permission is granted or not.
+ */
+ public boolean isGranted() {
+ return mGranted;
+ }
+
+ /**
+ * @return Permission flags.
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
new file mode 100644
index 0000000..91b4aaa
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageManagerInternal;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.permission.persistence.RuntimePermissionsPersistence;
+import com.android.permission.persistence.RuntimePermissionsState;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerLocal;
+import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.SharedUserApi;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provider of legacy permissions data for new permission subsystem.
+ *
+ * @hide
+ */
+public class PermissionMigrationHelperImpl implements PermissionMigrationHelper {
+ private static final String LOG_TAG = PermissionMigrationHelperImpl.class.getSimpleName();
+
+ /**
+ * @return legacy permission definitions.
+ */
+ @NonNull
+ public Map<String, LegacyPermission> getLegacyPermissions() {
+ PackageManagerInternal mPackageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ return toLegacyPermissions(
+ mPackageManagerInternal.getLegacyPermissions().getPermissions());
+ }
+
+ /**
+ * @return legacy permission trees.
+ */
+ @NonNull
+ public Map<String, LegacyPermission> getLegacyPermissionTrees() {
+ PackageManagerInternal mPackageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ return toLegacyPermissions(
+ mPackageManagerInternal.getLegacyPermissions().getPermissionTrees());
+ }
+
+ @NonNull
+ private Map<String, LegacyPermission> toLegacyPermissions(
+ List<com.android.server.pm.permission.LegacyPermission> legacyPermissions) {
+ Map<String, LegacyPermission> permissions = new ArrayMap<>();
+ legacyPermissions.forEach(legacyPermission -> {
+ LegacyPermission permission = new LegacyPermission(legacyPermission.getPermissionInfo(),
+ legacyPermission.getType());
+ permissions.put(legacyPermission.getPermissionInfo().name, permission);
+ });
+
+ return permissions;
+ }
+
+ /**
+ * @return permissions state for a user, i.e. map of appId to map of permission name and state.
+ */
+ @NonNull
+ public Map<Integer, Map<String, LegacyPermissionState>> getLegacyPermissionStates(int userId) {
+ RuntimePermissionsPersistence legacyPersistence =
+ RuntimePermissionsPersistence.createInstance();
+ Map<Integer, Map<String, LegacyPermissionState>> appIdPermissionStates = new ArrayMap<>();
+
+ RuntimePermissionsState legacyState = legacyPersistence.readForUser(UserHandle.of(userId));
+ PackageManagerLocal packageManagerLocal =
+ LocalManagerRegistry.getManager(PackageManagerLocal.class);
+
+ try (PackageManagerLocal.UnfilteredSnapshot snapshot =
+ packageManagerLocal.withUnfilteredSnapshot()) {
+ Map<String, PackageState> packageStates = snapshot.getPackageStates();
+ legacyState.getPackagePermissions().forEach((packageName, permissionStates) -> {
+ PackageState packageState = packageStates.get(packageName);
+ if (packageState != null) {
+ int appId = packageState.getAppId();
+ appIdPermissionStates.put(appId, toLegacyPermissionStates(permissionStates));
+ } else {
+ Log.w(LOG_TAG, "Package " + packageName + " not found.");
+ }
+ });
+
+ Map<String, SharedUserApi> sharedUsers = snapshot.getSharedUsers();
+ legacyState.getSharedUserPermissions().forEach((sharedUserName, permissionStates) -> {
+ SharedUserApi sharedUser = sharedUsers.get(sharedUserName);
+ if (sharedUser != null) {
+ int appId = sharedUser.getAppId();
+ appIdPermissionStates.put(appId, toLegacyPermissionStates(permissionStates));
+ } else {
+ Log.w(LOG_TAG, "Shared user " + sharedUserName + " not found.");
+ }
+ });
+ }
+ return appIdPermissionStates;
+ }
+
+ @NonNull
+ private Map<String, LegacyPermissionState> toLegacyPermissionStates(
+ List<RuntimePermissionsState.PermissionState> permissions) {
+ Map<String, LegacyPermissionState> legacyPermissions = new ArrayMap<>();
+
+ final int size = permissions.size();
+ for (int i = 0; i < size; i++) {
+ RuntimePermissionsState.PermissionState permState = permissions.get(i);
+ legacyPermissions.put(permState.getName(), new LegacyPermissionState(
+ permState.isGranted(), permState.getFlags()));
+ }
+
+ return legacyPermissions;
+ }
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b83d509..bf8cbea 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -40,7 +40,6 @@
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.SynchronousUserSwitchObserver;
import android.content.BroadcastReceiver;
@@ -317,7 +316,6 @@
private SettingsObserver mSettingsObserver;
private DreamManagerInternal mDreamManager;
private LogicalLight mAttentionLight;
- private ActivityManagerInternal mAmInternal;
private final InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
private final AmbientDisplaySuppressionController mAmbientDisplaySuppressionController;
@@ -1244,7 +1242,6 @@
mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
mPolicy = getLocalService(WindowManagerPolicy.class);
mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
- mAmInternal = getLocalService(ActivityManagerInternal.class);
mAttentionDetector.systemReady(mContext);
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
@@ -4088,8 +4085,9 @@
final UidState state = wakeLock.mUidState;
if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0 &&
- (mAmInternal != null && !mAmInternal.canHoldWakeLocksInDeepDoze(
- state.mUid, state.mProcState))) {
+ state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT &&
+ state.mProcState >
+ ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
disabled = true;
}
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 5096ad1..a319113 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -28,7 +28,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
-import android.media.AudioAttributes;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -41,6 +40,8 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -99,11 +100,6 @@
// static instance of this thread
private static final ShutdownThread sInstance = new ShutdownThread();
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
// Metrics that will be reported to tron after reboot
private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
@@ -700,7 +696,10 @@
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator(context);
try {
- vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
+ vibrator.vibrate(
+ VibrationEffect.createOneShot(
+ SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE),
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH));
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
diff --git a/services/core/java/com/android/server/security/FileIntegrity.java b/services/core/java/com/android/server/security/FileIntegrity.java
index 7b87d99..b8f187e 100644
--- a/services/core/java/com/android/server/security/FileIntegrity.java
+++ b/services/core/java/com/android/server/security/FileIntegrity.java
@@ -16,6 +16,8 @@
package com.android.server.security;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.ParcelFileDescriptor;
@@ -36,18 +38,26 @@
private FileIntegrity() {}
/**
- * Enables fs-verity, if supported by the filesystem.
+ * Enables fs-verity, if supported by the filesystem. This operation is atomic, i.e. it's either
+ * enabled or not, even in case of power failure during or after the call.
* @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html">
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public static void setUpFsVerity(@NonNull File file) throws IOException {
- VerityUtils.setUpFsverity(file.getAbsolutePath());
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, MODE_READ_ONLY)) {
+ setUpFsVerity(pfd);
+ }
}
/**
- * Enables fs-verity, if supported by the filesystem.
+ * Enables fs-verity, if supported by the filesystem. This operation is atomic, i.e. it's either
+ * enabled or not, even in case of power failure during or after the call.
* @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html">
+ *
+ * @param parcelFileDescriptor an FD opened in {@link ParcelFileDescriptor#MODE_READ_ONLY}.
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index cb7e54d..bf99772 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -412,7 +412,7 @@
if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
// Force update of user settings before checking if this vibration effect should
// be ignored or scaled.
- mVibrationSettings.mSettingObserver.onChange(false);
+ mVibrationSettings.update();
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 8a4fc0db..1f7af41 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -43,7 +43,7 @@
static final String DOC_LINK = "go/android-asm";
/** Used to determine which version of the ASM logic was used in logs while we iterate */
- static final int ASM_VERSION = 5;
+ static final int ASM_VERSION = 6;
private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e990460..ac07248 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1948,9 +1948,13 @@
boolean passesAsmChecks = true;
Task sourceTask = mSourceRecord.getTask();
- // Don't allow launches into a new task if the current task is not foreground.
+ // Allow launching into a new task (or a task matching the launched activity's
+ // affinity) only if the current task is foreground or mutating its own task.
+ // The latter can happen eg. if caller uses NEW_TASK flag and the activity being
+ // launched matches affinity of source task.
if (taskToFront) {
- passesAsmChecks = sourceTask != null && sourceTask.isVisible();
+ passesAsmChecks = sourceTask != null
+ && (sourceTask.isVisible() || sourceTask == targetTask);
}
if (passesAsmChecks) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 18be2a3..7d478b3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -45,6 +45,7 @@
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_CANT_SAVE_STATE;
+import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -401,6 +402,7 @@
volatile WindowProcessController mHeavyWeightProcess;
boolean mHasHeavyWeightFeature;
boolean mHasLeanbackFeature;
+ boolean mHasCompanionDeviceSetupFeature;
/** The process of the top most activity. */
volatile WindowProcessController mTopApp;
/**
@@ -859,6 +861,7 @@
final PackageManager pm = mContext.getPackageManager();
mHasHeavyWeightFeature = pm.hasSystemFeature(FEATURE_CANT_SAVE_STATE);
mHasLeanbackFeature = pm.hasSystemFeature(FEATURE_LEANBACK);
+ mHasCompanionDeviceSetupFeature = pm.hasSystemFeature(FEATURE_COMPANION_DEVICE_SETUP);
mVrController.onSystemReady();
mRecentTasks.onSystemReadyLocked();
mTaskSupervisor.onSystemReady();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e463358..df471c5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1239,8 +1239,13 @@
return Context.DEVICE_ID_DEFAULT;
}
if (mVirtualDeviceManager == null) {
- mVirtualDeviceManager =
- mService.mContext.getSystemService(VirtualDeviceManager.class);
+ if (mService.mHasCompanionDeviceSetupFeature) {
+ mVirtualDeviceManager =
+ mService.mContext.getSystemService(VirtualDeviceManager.class);
+ }
+ if (mVirtualDeviceManager == null) {
+ return Context.DEVICE_ID_DEFAULT;
+ }
}
return mVirtualDeviceManager.getDeviceIdForDisplayId(displayId);
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index dc674c2..44b1cc8 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -862,10 +862,9 @@
return mActivityRecord.mWmService.mContext.getResources();
}
- private void handleHorizontalDoubleTap(int x) {
- // TODO(b/260857308): Investigate if enabling reachability for translucent activity
- if (hasInheritedLetterboxBehavior() || !isHorizontalReachabilityEnabled()
- || mActivityRecord.isInTransition()) {
+ @VisibleForTesting
+ void handleHorizontalDoubleTap(int x) {
+ if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
@@ -903,10 +902,9 @@
mActivityRecord.recomputeConfiguration();
}
- private void handleVerticalDoubleTap(int y) {
- // TODO(b/260857308): Investigate if enabling reachability for translucent activity
- if (hasInheritedLetterboxBehavior() || !isVerticalReachabilityEnabled()
- || mActivityRecord.isInTransition()) {
+ @VisibleForTesting
+ void handleVerticalDoubleTap(int y) {
+ if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a68b3cb..bf760c9 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -53,6 +53,7 @@
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
@@ -2230,6 +2231,10 @@
.isMonitorTransitionTarget(topActivity)) {
flags |= TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
}
+ if (topActivity != null && topActivity.mLaunchTaskBehind) {
+ Slog.e(TAG, "Unexpected launch-task-behind operation in shell transition");
+ flags |= FLAG_TASK_LAUNCHING_BEHIND;
+ }
} else {
if (task.mAtmService.mBackNavigationController
.isMonitorTransitionTarget(task)) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8e22821..3464a72 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -18,7 +18,7 @@
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.isStartResultSuccessful;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
@@ -539,9 +539,9 @@
// setWindowingMode call in force-hidden.
boolean forceHiddenForPip = false;
if (wc.asTask() != null && wc.inPinnedWindowingMode()
- && entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {
- // We are in pip and going to undefined. Now search hierarchy ops to determine
- // whether we are removing pip or expanding pip.
+ && entry.getValue().getWindowingMode() != WINDOWING_MODE_PINNED) {
+ // We are going out of pip. Now search hierarchy ops to determine whether we
+ // are removing pip or expanding pip.
for (int i = 0; i < hopSize; ++i) {
final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;
@@ -677,7 +677,7 @@
+ " windowing mode during locked task mode.");
}
- if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) {
+ if (windowingMode == WINDOWING_MODE_PINNED) {
// Do not directly put the container into PINNED mode as it may not support it or
// the app may not want to enter it. Instead, send a signal to request PIP
// mode to the app if they wish to support it below in #applyTaskChanges.
@@ -729,7 +729,7 @@
tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
}
- if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED
+ if (c.getWindowingMode() == WINDOWING_MODE_PINNED
&& !tr.inPinnedWindowingMode()) {
final ActivityRecord activity = tr.getTopNonFinishingActivity();
if (activity != null) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 7c0318d..aa10291 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -210,7 +210,7 @@
/** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */
private volatile boolean mHasCachedConfiguration;
- private int mTopActivityDeviceId = Context.DEVICE_ID_DEFAULT;
+ private int mLastTopActivityDeviceId = Context.DEVICE_ID_DEFAULT;
/**
* Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not
* registered.
@@ -1411,8 +1411,9 @@
// If deviceId for the top-activity changed, schedule passing it to the app process.
boolean topActivityDeviceChanged = false;
int deviceId = getTopActivityDeviceId();
- if (deviceId != mTopActivityDeviceId) {
+ if (deviceId != mLastTopActivityDeviceId) {
topActivityDeviceChanged = true;
+ mLastTopActivityDeviceId = deviceId;
}
final Configuration config = getConfiguration();
@@ -1431,15 +1432,12 @@
return;
}
- // TODO(b/263402938): Add tests that capture the deviceId dispatch to the client.
- mTopActivityDeviceId = deviceId;
- dispatchConfiguration(config, topActivityDeviceChanged ? mTopActivityDeviceId
- : Context.DEVICE_ID_INVALID);
+ dispatchConfiguration(config);
}
private int getTopActivityDeviceId() {
ActivityRecord topActivity = getTopNonFinishingActivity();
- int updatedDeviceId = mTopActivityDeviceId;
+ int updatedDeviceId = Context.DEVICE_ID_DEFAULT;
if (topActivity != null && topActivity.mDisplayContent != null) {
updatedDeviceId = mAtm.mTaskSupervisor.getDeviceIdForDisplayId(
topActivity.mDisplayContent.mDisplayId);
@@ -1484,10 +1482,6 @@
}
void dispatchConfiguration(Configuration config) {
- dispatchConfiguration(config, getTopActivityDeviceId());
- }
-
- void dispatchConfiguration(Configuration config, int deviceId) {
mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1514,16 +1508,10 @@
}
}
- scheduleConfigurationChange(mThread, config, deviceId);
+ scheduleConfigurationChange(mThread, config);
}
private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) {
- // By default send invalid deviceId as no-op signal so it's not updated on the client side.
- scheduleConfigurationChange(thread, config, Context.DEVICE_ID_INVALID);
- }
-
- private void scheduleConfigurationChange(IApplicationThread thread, Configuration config,
- int deviceId) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName,
config);
if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1533,7 +1521,7 @@
mHasCachedConfiguration = false;
try {
mAtm.getLifecycleManager().scheduleTransaction(thread,
- ConfigurationChangeItem.obtain(config, deviceId));
+ ConfigurationChangeItem.obtain(config, mLastTopActivityDeviceId));
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e);
}
diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp
index 3677641..b57e451 100644
--- a/services/core/jni/gnss/GnssConfiguration.cpp
+++ b/services/core/jni/gnss/GnssConfiguration.cpp
@@ -67,7 +67,7 @@
: mIGnssConfiguration(iGnssConfiguration) {}
jobject GnssConfiguration::getVersion(JNIEnv* env) {
- return createHalInterfaceVersionJavaObject(env, 3, 0);
+ return createHalInterfaceVersionJavaObject(env, 3, mIGnssConfiguration->getInterfaceVersion());
}
jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) {
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index f8b1e6fc..6bf18c2 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -29,6 +29,9 @@
import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
+
import java.util.ArrayList;
/**
@@ -119,18 +122,21 @@
// TODO: Differentiate btw cancelled and false
mChosenProviderMetric.setChosenProviderStatus(
MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
- logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onSuccess();
- logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_SUCCESS);
} catch (RemoteException e) {
mChosenProviderMetric.setChosenProviderStatus(
MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE);
Log.i(TAG, "Issue while propagating the response to the client");
- logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -139,7 +145,8 @@
Log.i(TAG, "respondToClientWithErrorAndFinish");
if (isSessionCancelled()) {
// TODO: Differentiate btw cancelled and false
- logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -148,7 +155,8 @@
} catch (RemoteException e) {
e.printStackTrace();
}
- logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_FAILURE);
finishSession(/*propagateCancellation=*/false);
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 4dd0c84..656e44c 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -36,6 +36,9 @@
import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
+
import java.util.ArrayList;
/**
@@ -141,16 +144,19 @@
}
if (isSessionCancelled()) {
// TODO: Differentiate btw cancelled and false
- logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onResponse(response);
- logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_SUCCESS);
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client: " + e.getMessage());
- logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -163,7 +169,8 @@
}
if (isSessionCancelled()) {
// TODO: Differentiate btw cancelled and false
- logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -172,10 +179,20 @@
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client: " + e.getMessage());
}
- logApiCalled(RequestType.CREATE_CREDENTIALS, /* isSuccessful */ false);
+ logFailureOrUserCancel(errorType);
finishSession(/*propagateCancellation=*/false);
}
+ private void logFailureOrUserCancel(String errorType) {
+ if (CreateCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
+ logApiCall(ApiName.CREATE_CREDENTIAL,
+ /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+ } else {
+ logApiCall(ApiName.CREATE_CREDENTIAL,
+ /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+ }
+ }
+
@Override
public void onProviderStatusChanged(ProviderSession.Status status,
ComponentName componentName) {
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 7a4e7df..41ae911 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -64,6 +64,8 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.SecureSettingsServiceNameResolver;
@@ -84,7 +86,7 @@
*/
public final class CredentialManagerService
extends AbstractMasterSystemService<
- CredentialManagerService, CredentialManagerServiceImpl> {
+ CredentialManagerService, CredentialManagerServiceImpl> {
private static final String TAG = "CredManSysService";
private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
@@ -267,7 +269,8 @@
final long origId = Binder.clearCallingIdentity();
try {
return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, false);
+ DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+ false);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -317,8 +320,8 @@
Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>> result =
new HashSet<>();
- for (CredentialDescriptionRegistry.FilterResult filterResult: filterResults) {
- for (CredentialOption credentialOption: options) {
+ for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
+ for (CredentialOption credentialOption : options) {
if (filterResult.mFlattenedRequest.equals(credentialOption
.getCredentialRetrievalData()
.getString(CredentialOption.FLATTENED_REQUEST))) {
@@ -364,13 +367,13 @@
CallingAppInfo callingAppInfo;
try {
packageInfo =
- getContext()
- .getPackageManager()
- .getPackageInfoAsUser(
- realPackageName,
- PackageManager.PackageInfoFlags.of(
- PackageManager.GET_SIGNING_CERTIFICATES),
- userId);
+ getContext()
+ .getPackageManager()
+ .getPackageInfoAsUser(
+ realPackageName,
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.GET_SIGNING_CERTIFICATES),
+ userId);
callingAppInfo = new CallingAppInfo(realPackageName, packageInfo.signingInfo, origin);
} catch (PackageManager.NameNotFoundException e) {
Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
@@ -421,41 +424,41 @@
if (isCredentialDescriptionApiEnabled()) {
List<CredentialOption> optionsThatRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- getCredentialOption ->
- !TextUtils.isEmpty(
- getCredentialOption
- .getCredentialRetrievalData()
- .getString(
- CredentialOption
- .FLATTENED_REQUEST,
- null)))
- .toList();
+ .filter(
+ getCredentialOption ->
+ !TextUtils.isEmpty(
+ getCredentialOption
+ .getCredentialRetrievalData()
+ .getString(
+ CredentialOption
+ .FLATTENED_REQUEST,
+ null)))
+ .toList();
List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- getCredentialOption ->
- TextUtils.isEmpty(
- getCredentialOption
- .getCredentialRetrievalData()
- .getString(
- CredentialOption
- .FLATTENED_REQUEST,
- null)))
- .toList();
+ .filter(
+ getCredentialOption ->
+ TextUtils.isEmpty(
+ getCredentialOption
+ .getCredentialRetrievalData()
+ .getString(
+ CredentialOption
+ .FLATTENED_REQUEST,
+ null)))
+ .toList();
List<ProviderSession> sessionsWithoutRemoteService =
initiateProviderSessionsWithActiveContainers(
- session,
- getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
+ session,
+ getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
List<ProviderSession> sessionsWithRemoteService =
initiateProviderSessions(
- session,
- optionsThatDoNotRequireActiveCredentials.stream()
- .map(CredentialOption::getType)
- .collect(Collectors.toList()));
+ session,
+ optionsThatDoNotRequireActiveCredentials.stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
Set<ProviderSession> all = new LinkedHashSet<>();
all.addAll(sessionsWithRemoteService);
@@ -465,11 +468,11 @@
} else {
// Initiate all provider sessions
providerSessions =
- initiateProviderSessions(
- session,
- request.getCredentialOptions().stream()
- .map(CredentialOption::getType)
- .collect(Collectors.toList()));
+ initiateProviderSessions(
+ session,
+ request.getCredentialOptions().stream()
+ .map(CredentialOption::getType)
+ .collect(Collectors.toList()));
}
if (providerSessions.isEmpty()) {
@@ -481,8 +484,8 @@
Log.i(
TAG,
"Issue invoking onError on IGetCredentialCallback "
- + "callback: "
- + e.getMessage());
+ + "callback: "
+ + e.getMessage());
}
}
providerSessions.forEach(ProviderSession::invokeSession);
@@ -538,8 +541,8 @@
Log.i(
TAG,
"Issue invoking onError on ICreateCredentialCallback "
- + "callback: "
- + e.getMessage());
+ + "callback: "
+ + e.getMessage());
}
}
@@ -654,13 +657,17 @@
if (serviceComponentName.equals(componentName)) {
if (!s.getServicePackageName().equals(callingPackage)) {
// The component name and the package name do not match.
+ MetricUtilities.logApiCalled(
+ ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
+ ApiStatus.METRICS_API_STATUS_FAILURE, callingUid);
Log.w(
TAG,
"isEnabledCredentialProviderService: Component name does not"
+ " match package name.");
return false;
}
-
+ MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
+ ApiStatus.METRICS_API_STATUS_SUCCESS, callingUid);
return true;
}
}
@@ -745,7 +752,7 @@
enforceCallingPackage(callingPackage, Binder.getCallingUid());
List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
+ getServicesForCredentialDescription(UserHandle.getCallingUserId());
List<String> providers =
services.stream()
@@ -801,7 +808,7 @@
enforceCallingPackage(callingPackage, Binder.getCallingUid());
List<CredentialProviderInfo> services =
- getServicesForCredentialDescription(UserHandle.getCallingUserId());
+ getServicesForCredentialDescription(UserHandle.getCallingUserId());
List<String> providers =
services.stream()
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 32b14d7..ce26c88 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -34,6 +34,9 @@
import android.service.credentials.CredentialProviderInfo;
import android.util.Log;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
+
import java.util.ArrayList;
/**
@@ -117,16 +120,19 @@
}
if (isSessionCancelled()) {
// TODO: Differentiate btw cancelled and false
- logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
try {
mClientCallback.onResponse(response);
- logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ true);
+ logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_SUCCESS);
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client with a response : " + e.getMessage());
- logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_FAILURE);
}
finishSession(/*propagateCancellation=*/false);
}
@@ -137,7 +143,8 @@
return;
}
if (isSessionCancelled()) {
- logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
+ logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
+ ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
return;
}
@@ -147,10 +154,20 @@
} catch (RemoteException e) {
Log.i(TAG, "Issue while responding to client with error : " + e.getMessage());
}
- logApiCalled(RequestType.GET_CREDENTIALS, /* isSuccessful */ false);
+ logFailureOrUserCancel(errorType);
finishSession(/*propagateCancellation=*/false);
}
+ private void logFailureOrUserCancel(String errorType) {
+ if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
+ logApiCall(ApiName.GET_CREDENTIAL,
+ /* apiStatus */ ApiStatus.METRICS_API_STATUS_USER_CANCELED);
+ } else {
+ logApiCall(ApiName.GET_CREDENTIAL,
+ /* apiStatus */ ApiStatus.METRICS_API_STATUS_FAILURE);
+ }
+ }
+
@Override
public void onUiCancellation(boolean isUserCancellation) {
if (isUserCancellation) {
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 27d9836..f75a9b6 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -16,14 +16,6 @@
package com.android.server.credentials;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
@@ -35,6 +27,14 @@
import android.content.pm.PackageManager;
import android.util.Log;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
+import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.ChosenProviderMetric;
+
+import java.util.Map;
+
/**
* For all future metric additions, this will contain their names for local usage after importing
* from {@link com.android.internal.util.FrameworkStatsLog}.
@@ -43,24 +43,10 @@
private static final String TAG = "MetricUtilities";
- // Metrics constants
- protected static final int METRICS_API_NAME_UNKNOWN =
- CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
- protected static final int METRICS_API_NAME_GET_CREDENTIAL =
- CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
- protected static final int METRICS_API_NAME_CREATE_CREDENTIAL =
- CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
- protected static final int METRICS_API_NAME_CLEAR_CREDENTIAL =
- CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
- // TODO add isEnabled
- protected static final int METRICS_API_STATUS_SUCCESS =
- CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
- protected static final int METRICS_API_STATUS_FAILURE =
- CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
- protected static final int METRICS_API_STATUS_CLIENT_CANCEL =
- CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED;
- protected static final int METRICS_API_STATUS_USER_CANCEL =
- CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
+ private static final int DEFAULT_INT_32 = -1;
+ private static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
+
+ // Metrics constants TODO(b/269290341) migrate to enums eventually to improve
protected static final int METRICS_PROVIDER_STATUS_FINAL_FAILURE =
CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
protected static final int METRICS_PROVIDER_STATUS_QUERY_FAILURE =
@@ -77,6 +63,7 @@
* This retrieves the uid of any package name, given a context and a component name for the
* package. By default, if the desired package uid cannot be found, it will fall back to a
* bogus uid.
+ *
* @return the uid of a given package
*/
protected static int getPackageUid(Context context, ComponentName componentName) {
@@ -92,4 +79,75 @@
return sessUid;
}
+ /**
+ * The most common logging helper, handles the overall status of the API request with the
+ * provider status and latencies. Other versions of this method may be more useful depending
+ * on the situation, as this is geared towards the logging of {@link ProviderSession} types.
+ *
+ * @param apiName the api type to log
+ * @param apiStatus the api status to log
+ * @param providers a map with known providers
+ * @param callingUid the calling UID of the client app
+ * @param chosenProviderMetric the metric data type of the final chosen provider
+ */
+ protected static void logApiCalled(ApiName apiName, ApiStatus apiStatus,
+ Map<String, ProviderSession> providers, int callingUid,
+ ChosenProviderMetric chosenProviderMetric) {
+ var providerSessions = providers.values();
+ int providerSize = providerSessions.size();
+ int[] candidateUidList = new int[providerSize];
+ int[] candidateQueryRoundTripTimeList = new int[providerSize];
+ int[] candidateStatusList = new int[providerSize];
+ int index = 0;
+ for (var session : providerSessions) {
+ CandidateProviderMetric metric = session.mCandidateProviderMetric;
+ candidateUidList[index] = metric.getCandidateUid();
+ candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs();
+ candidateStatusList[index] = metric.getProviderQueryStatus();
+ index++;
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED,
+ /* api_name */apiName.getMetricCode(),
+ /* caller_uid */ callingUid,
+ /* api_status */ apiStatus.getMetricCode(),
+ /* repeated_candidate_provider_uid */ candidateUidList,
+ /* repeated_candidate_provider_round_trip_time_query_microseconds */
+ candidateQueryRoundTripTimeList,
+ /* repeated_candidate_provider_status */ candidateStatusList,
+ /* chosen_provider_uid */ chosenProviderMetric.getChosenUid(),
+ /* chosen_provider_round_trip_time_overall_microseconds */
+ chosenProviderMetric.getEntireProviderLatencyMs(),
+ /* chosen_provider_final_phase_microseconds */
+ chosenProviderMetric.getFinalPhaseLatencyMs(),
+ /* chosen_provider_status */ chosenProviderMetric.getChosenProviderStatus());
+ }
+
+ /**
+ * This is useful just to record an API calls' final event, and for no other purpose. It will
+ * contain default values for all other optional parameters.
+ *
+ * TODO(b/271135048) - given space requirements, this may be a good candidate for another atom
+ *
+ * @param apiName the api name to log
+ * @param apiStatus the status to log
+ * @param callingUid the calling uid
+ */
+ protected static void logApiCalled(ApiName apiName, ApiStatus apiStatus,
+ int callingUid) {
+ FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED,
+ /* api_name */apiName.getMetricCode(),
+ /* caller_uid */ callingUid,
+ /* api_status */ apiStatus.getMetricCode(),
+ /* repeated_candidate_provider_uid */ DEFAULT_REPEATED_INT_32,
+ /* repeated_candidate_provider_round_trip_time_query_microseconds */
+ DEFAULT_REPEATED_INT_32,
+ /* repeated_candidate_provider_status */ DEFAULT_REPEATED_INT_32,
+ /* chosen_provider_uid */ DEFAULT_INT_32,
+ /* chosen_provider_round_trip_time_overall_microseconds */
+ DEFAULT_INT_32,
+ /* chosen_provider_final_phase_microseconds */
+ DEFAULT_INT_32,
+ /* chosen_provider_status */ DEFAULT_INT_32);
+ }
+
}
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index f8bbfcf..86e05cf 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -16,12 +16,7 @@
package com.android.server.credentials;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CLEAR_CREDENTIAL;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CREATE_CREDENTIAL;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_GET_CREDENTIAL;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_UNKNOWN;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_FAILURE;
-import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_SUCCESS;
+import static com.android.server.credentials.MetricUtilities.logApiCalled;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -39,7 +34,8 @@
import android.util.Log;
import com.android.internal.R;
-import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.credentials.metrics.ApiName;
+import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.credentials.metrics.CandidateProviderMetric;
import com.android.server.credentials.metrics.ChosenProviderMetric;
@@ -162,50 +158,10 @@
}
return false;
}
- // TODO: move these definitions to a separate logging focused class.
- enum RequestType {
- GET_CREDENTIALS,
- CREATE_CREDENTIALS,
- CLEAR_CREDENTIALS,
- }
- private static int getApiNameFromRequestType(RequestType requestType) {
- switch (requestType) {
- case GET_CREDENTIALS:
- return METRICS_API_NAME_GET_CREDENTIAL;
- case CREATE_CREDENTIALS:
- return METRICS_API_NAME_CREATE_CREDENTIAL;
- case CLEAR_CREDENTIALS:
- return METRICS_API_NAME_CLEAR_CREDENTIAL;
- default:
- return METRICS_API_NAME_UNKNOWN;
- }
- }
-
- protected void logApiCalled(RequestType requestType, boolean isSuccessfulOverall) {
- var providerSessions = mProviders.values();
- int providerSize = providerSessions.size();
- int[] candidateUidList = new int[providerSize];
- int[] candidateQueryRoundTripTimeList = new int[providerSize];
- int[] candidateStatusList = new int[providerSize];
- int index = 0;
- for (var session : providerSessions) {
- CandidateProviderMetric metric = session.mCandidateProviderMetric;
- candidateUidList[index] = metric.getCandidateUid();
- candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs();
- candidateStatusList[index] = metric.getProviderQueryStatus();
- index++;
- }
- FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED,
- /* api_name */getApiNameFromRequestType(requestType), /* caller_uid */
- mCallingUid, /* api_status */
- isSuccessfulOverall ? METRICS_API_STATUS_SUCCESS : METRICS_API_STATUS_FAILURE,
- candidateUidList,
- candidateQueryRoundTripTimeList,
- candidateStatusList, mChosenProviderMetric.getChosenUid(),
- mChosenProviderMetric.getEntireProviderLatencyMs(),
- mChosenProviderMetric.getFinalPhaseLatencyMs(),
- mChosenProviderMetric.getChosenProviderStatus());
+ protected void logApiCall(ApiName apiName, ApiStatus apiStatus) {
+ logApiCalled(apiName, apiStatus, mProviders, mCallingUid,
+ mChosenProviderMetric);
}
protected boolean isSessionCancelled() {
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
new file mode 100644
index 0000000..d4b51df
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials.metrics;
+
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
+
+public enum ApiName {
+ UNKNOWN(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN),
+ GET_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL),
+ CREATE_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL),
+ CLEAR_CREDENTIAL(CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL),
+ IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE(
+ CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE
+ );
+
+ private final int mInnerMetricCode;
+
+ ApiName(int innerMetricCode) {
+ this.mInnerMetricCode = innerMetricCode;
+ }
+
+ /**
+ * Gives the West-world version of the metric name.
+ *
+ * @return a code corresponding to the west world metric name
+ */
+ public int getMetricCode() {
+ return this.mInnerMetricCode;
+ }
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
new file mode 100644
index 0000000..36a1f2d
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials.metrics;
+
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
+
+public enum ApiStatus {
+ METRICS_API_STATUS_SUCCESS(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS),
+ METRICS_API_STATUS_FAILURE(CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE),
+ METRICS_API_STATUS_CLIENT_CANCELED(
+ CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED),
+ METRICS_API_STATUS_USER_CANCELED(
+ CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED);
+
+ private final int mInnerMetricCode;
+
+ ApiStatus(int innerMetricCode) {
+ this.mInnerMetricCode = innerMetricCode;
+ }
+
+ /**
+ * Gives the West-world version of the metric name.
+ *
+ * @return a code corresponding to the west world metric name
+ */
+ public int getMetricCode() {
+ return this.mInnerMetricCode;
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 850b5b6..321a71e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -173,6 +173,8 @@
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.dex.OdsignStatsLogger;
+import com.android.server.pm.permission.PermissionMigrationHelper;
+import com.android.server.pm.permission.PermissionMigrationHelperImpl;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.policy.AppOpsPolicy;
import com.android.server.policy.PermissionPolicyService;
@@ -1132,6 +1134,8 @@
// Start AccessCheckingService which provides new implementation for permission and app op.
t.traceBegin("StartAccessCheckingService");
+ LocalServices.addService(PermissionMigrationHelper.class,
+ new PermissionMigrationHelperImpl());
mSystemServiceManager.startService(AccessCheckingService.class);
t.traceEnd();
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 17e1744..0ca4dfc 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -113,7 +113,6 @@
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
@VisibleForTesting
static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
- private static final int DEBOUNCE_LENGTH_MS = 500;
private final Context mContext;
private final Injector mInjector;
@@ -130,7 +129,6 @@
private final List<PeopleService.ConversationsListener> mConversationsListeners =
new ArrayList<>(1);
private final Handler mHandler;
- private final PerPackageThrottler mShortcutsThrottler;
private ContentObserver mCallLogContentObserver;
private ContentObserver mMmsSmsContentObserver;
@@ -142,17 +140,14 @@
private ConversationStatusExpirationBroadcastReceiver mStatusExpReceiver;
public DataManager(Context context) {
- this(context, new Injector(), BackgroundThread.get().getLooper(),
- new PerPackageThrottlerImpl(BackgroundThread.getHandler(), DEBOUNCE_LENGTH_MS));
+ this(context, new Injector(), BackgroundThread.get().getLooper());
}
- DataManager(Context context, Injector injector, Looper looper,
- PerPackageThrottler shortcutsThrottler) {
+ DataManager(Context context, Injector injector, Looper looper) {
mContext = context;
mInjector = injector;
mScheduledExecutor = mInjector.createScheduledExecutor();
mHandler = new Handler(looper);
- mShortcutsThrottler = shortcutsThrottler;
}
/** Initialization. Called when the system services are up running. */
@@ -856,12 +851,12 @@
// pair of <package name, conversation info>
List<Pair<String, ConversationInfo>> cachedConvos = new ArrayList<>();
userData.forAllPackages(packageData -> {
- packageData.forAllConversations(conversationInfo -> {
- if (isEligibleForCleanUp(conversationInfo)) {
- cachedConvos.add(
- Pair.create(packageData.getPackageName(), conversationInfo));
- }
- });
+ packageData.forAllConversations(conversationInfo -> {
+ if (isEligibleForCleanUp(conversationInfo)) {
+ cachedConvos.add(
+ Pair.create(packageData.getPackageName(), conversationInfo));
+ }
+ });
});
if (cachedConvos.size() <= targetCachedCount) {
return;
@@ -872,8 +867,8 @@
numToUncache + 1,
Comparator.comparingLong((Pair<String, ConversationInfo> pair) ->
Math.max(
- pair.second.getLastEventTimestamp(),
- pair.second.getCreationTimestamp())).reversed());
+ pair.second.getLastEventTimestamp(),
+ pair.second.getCreationTimestamp())).reversed());
for (Pair<String, ConversationInfo> cached : cachedConvos) {
if (hasActiveNotifications(cached.first, userId, cached.second.getShortcutId())) {
continue;
@@ -1109,35 +1104,26 @@
@Override
public void onShortcutsAddedOrUpdated(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
- mShortcutsThrottler.scheduleDebounced(
- new Pair<>(packageName, user.getIdentifier()),
- () -> {
- PackageData packageData = getPackage(packageName, user.getIdentifier());
- List<ShortcutInfo> queriedShortcuts = getShortcuts(packageName,
- user.getIdentifier(), null);
- boolean hasCachedShortcut = false;
- for (ShortcutInfo shortcut : queriedShortcuts) {
- if (ShortcutHelper.isConversationShortcut(
- shortcut, mShortcutServiceInternal, user.getIdentifier())) {
- if (shortcut.isCached()) {
- ConversationInfo info = packageData != null
- ? packageData.getConversationInfo(shortcut.getId())
- : null;
- if (info == null
- || !info.isShortcutCachedForNotification()) {
- hasCachedShortcut = true;
- }
- }
- addOrUpdateConversationInfo(shortcut);
+ mInjector.getBackgroundExecutor().execute(() -> {
+ PackageData packageData = getPackage(packageName, user.getIdentifier());
+ for (ShortcutInfo shortcut : shortcuts) {
+ if (ShortcutHelper.isConversationShortcut(
+ shortcut, mShortcutServiceInternal, user.getIdentifier())) {
+ if (shortcut.isCached()) {
+ ConversationInfo conversationInfo = packageData != null
+ ? packageData.getConversationInfo(shortcut.getId()) : null;
+ if (conversationInfo == null
+ || !conversationInfo.isShortcutCachedForNotification()) {
+ // This is a newly cached shortcut. Clean up the existing cached
+ // shortcuts to ensure the cache size is under the limit.
+ cleanupCachedShortcuts(user.getIdentifier(),
+ MAX_CACHED_RECENT_SHORTCUTS - 1);
}
}
- // Added at least one new conversation. Uncache older existing cached
- // shortcuts to ensure the cache size is under the limit.
- if (hasCachedShortcut) {
- cleanupCachedShortcuts(user.getIdentifier(),
- MAX_CACHED_RECENT_SHORTCUTS);
- }
- });
+ addOrUpdateConversationInfo(shortcut);
+ }
+ }
+ });
}
@Override
diff --git a/services/people/java/com/android/server/people/data/PerPackageThrottler.java b/services/people/java/com/android/server/people/data/PerPackageThrottler.java
deleted file mode 100644
index 3d6cd84..0000000
--- a/services/people/java/com/android/server/people/data/PerPackageThrottler.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.people.data;
-
-import android.util.Pair;
-
-/** The interface for throttling expensive runnables per package. */
-interface PerPackageThrottler {
- /**
- * Schedule a runnable to run in the future, and debounce runnables for same {@code pkgUserId}
- * that occur until that future has run.
- */
- void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable);
-}
diff --git a/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java b/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java
deleted file mode 100644
index fa5a67b..0000000
--- a/services/people/java/com/android/server/people/data/PerPackageThrottlerImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.people.data;
-
-import android.os.Handler;
-import android.util.Pair;
-
-import java.util.HashSet;
-
-/**
- * A class that implements a per-package throttler that prevents a runnable from executing more than
- * once every {@code debounceTime}.
- */
-public class PerPackageThrottlerImpl implements PerPackageThrottler {
- private final Handler mBackgroundHandler;
- private final int mDebounceTime;
- private final HashSet<Pair<String, Integer>> mPkgScheduledTasks = new HashSet<>();
-
- PerPackageThrottlerImpl(Handler backgroundHandler, int debounceTime) {
- mBackgroundHandler = backgroundHandler;
- mDebounceTime = debounceTime;
- }
-
- @Override
- public synchronized void scheduleDebounced(
- Pair<String, Integer> pkgUserId, Runnable runnable) {
- if (mPkgScheduledTasks.contains(pkgUserId)) {
- return;
- }
- mPkgScheduledTasks.add(pkgUserId);
- mBackgroundHandler.postDelayed(() -> {
- synchronized (this) {
- mPkgScheduledTasks.remove(pkgUserId);
- runnable.run();
- }
- }, mDebounceTime);
- }
-}
diff --git a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
index a25b720..5b1e4ef 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
@@ -53,6 +53,9 @@
writeHandler = WriteHandler(BackgroundThread.getHandler().looper)
}
+ /**
+ * Reads the state either from the disk or migrate legacy data when the data files are missing.
+ */
fun read(state: AccessState) {
readSystemState(state)
state.systemState.userIds.forEachIndexed { _, userId ->
@@ -61,28 +64,50 @@
}
private fun readSystemState(state: AccessState) {
- systemFile.parse {
+ val fileExists = systemFile.parse {
// This is the canonical way to call an extension function in a different class.
// TODO(b/259469752): Use context receiver for this when it becomes stable.
with(policy) { parseSystemState(state) }
}
- }
- private fun readUserState(state: AccessState, userId: Int) {
- getUserFile(userId).parse {
- with(policy) { parseUserState(state, userId) }
+ if (!fileExists) {
+ policy.migrateSystemState(state)
+ state.systemState.apply {
+ requestWrite()
+ write(state, UserHandle.USER_ALL)
+ }
}
}
- private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit) {
+
+ private fun readUserState(state: AccessState, userId: Int) {
+ val fileExists = getUserFile(userId).parse {
+ with(policy) { parseUserState(state, userId) }
+ }
+
+ if (!fileExists) {
+ policy.migrateUserState(state, userId)
+ state.userStates[userId].apply {
+ requestWrite()
+ write(state, userId)
+ }
+ }
+ }
+
+ /**
+ * @return {@code true} if the file is successfully read from the disk; {@code false} if
+ * the file doesn't exist yet.
+ */
+ private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit): Boolean =
try {
AtomicFile(this).read { it.parseBinaryXml(block) }
+ true
} catch (e: FileNotFoundException) {
Log.i(LOG_TAG, "$this not found")
+ false
} catch (e: Exception) {
throw IllegalStateException("Failed to read $this", e)
}
- }
fun write(state: AccessState) {
state.systemState.write(state, UserHandle.USER_ALL)
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 07a5e72..61574d4 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -262,6 +262,19 @@
}
}
+ fun migrateSystemState(state: AccessState) {
+ forEachSchemePolicy {
+ with(it) { migrateSystemState(state) }
+ }
+ }
+
+ fun migrateUserState(state: AccessState, userId: Int) {
+ forEachSchemePolicy {
+ with(it) { migrateUserState(state, userId) }
+ }
+ }
+
+
fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
forEachTag {
when (tagName) {
@@ -371,6 +384,10 @@
open fun MutateStateScope.onSystemReady() {}
+ open fun migrateSystemState(state: AccessState) {}
+
+ open fun migrateUserState(state: AccessState, userId: Int) {}
+
open fun BinaryXmlPullParser.parseSystemState(state: AccessState) {}
open fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index 48658ff..84da9b6 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -22,6 +22,7 @@
import android.os.Build
import android.permission.PermissionManager
import com.android.server.permission.access.util.andInv
+import com.android.server.permission.access.util.flagsToString
import com.android.server.permission.access.util.hasAnyBit
import com.android.server.permission.access.util.hasBits
@@ -477,4 +478,38 @@
}
return flags
}
+
+ fun flagToString(flag: Int): String =
+ when (flag) {
+ INSTALL_GRANTED -> "INSTALL_GRANTED"
+ INSTALL_REVOKED -> "INSTALL_REVOKED"
+ PROTECTION_GRANTED -> "PROTECTION_GRANTED"
+ ROLE -> "ROLE"
+ RUNTIME_GRANTED -> "RUNTIME_GRANTED"
+ USER_SET -> "USER_SET"
+ USER_FIXED -> "USER_FIXED"
+ POLICY_FIXED -> "POLICY_FIXED"
+ SYSTEM_FIXED -> "SYSTEM_FIXED"
+ PREGRANT -> "PREGRANT"
+ LEGACY_GRANTED -> "LEGACY_GRANTED"
+ IMPLICIT_GRANTED -> "IMPLICIT_GRANTED"
+ IMPLICIT -> "IMPLICIT"
+ USER_SENSITIVE_WHEN_GRANTED -> "USER_SENSITIVE_WHEN_GRANTED"
+ USER_SENSITIVE_WHEN_REVOKED -> "USER_SENSITIVE_WHEN_REVOKED"
+ INSTALLER_EXEMPT -> "INSTALLER_EXEMPT"
+ SYSTEM_EXEMPT -> "SYSTEM_EXEMPT"
+ UPGRADE_EXEMPT -> "UPGRADE_EXEMPT"
+ RESTRICTION_REVOKED -> "RESTRICTION_REVOKED"
+ SOFT_RESTRICTED -> "SOFT_RESTRICTED"
+ APP_OP_REVOKED -> "APP_OP_REVOKED"
+ ONE_TIME -> "ONE_TIME"
+ HIBERNATION -> "HIBERNATION"
+ USER_SELECTED -> "USER_SELECTED"
+ else -> "0x${flag.toUInt().toString(16).uppercase()}"
+ }
+
+ fun toString(flags: Int): String = flags.flagsToString { flagToString(it) }
+
+ fun apiFlagsToString(apiFlags: Int): String =
+ apiFlags.flagsToString { PackageManager.permissionFlagToString(it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index de7dc3b..7929245 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -1600,11 +1600,23 @@
}
override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) {
- // TODO("Not yet implemented")
+ service.mutateState {
+ with(policy) {
+ resetRuntimePermissions(androidPackage.packageName, userId)
+ }
+ }
}
override fun resetRuntimePermissionsForUser(userId: Int) {
- // TODO("Not yet implemented")
+ packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+ service.mutateState {
+ snapshot.packageStates.forEach { (_, packageState) ->
+ with(policy) {
+ resetRuntimePermissions(packageState.packageName, userId)
+ }
+ }
+ }
+ }
}
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionMigration.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionMigration.kt
new file mode 100644
index 0000000..8a7697c
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionMigration.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.permission
+
+import android.util.Log
+import com.android.server.LocalServices
+import com.android.server.permission.access.AccessState
+import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
+import com.android.server.pm.permission.PermissionMigrationHelper
+
+/**
+ * This class migrate legacy permissions to unified permission subsystem
+ */
+class UidPermissionMigration {
+ internal fun migrateSystemState(state: AccessState) {
+ val legacyPermissionsManager =
+ LocalServices.getService(PermissionMigrationHelper::class.java)!!
+ migratePermissions(state.systemState.permissions,
+ legacyPermissionsManager.legacyPermissions)
+ migratePermissions(state.systemState.permissionTrees,
+ legacyPermissionsManager.legacyPermissionTrees, true)
+ }
+
+ private fun migratePermissions(
+ permissions: IndexedMap<String, Permission>,
+ legacyPermissions: Map<String, PermissionMigrationHelper.LegacyPermission>,
+ isPermissionTree: Boolean = false
+ ) {
+ legacyPermissions.forEach { (_, legacyPermission) ->
+ val permission = Permission(
+ legacyPermission.permissionInfo, false, legacyPermission.type, 0
+ )
+ permissions[permission.name] = permission
+ if (DEBUG_MIGRATION) {
+ Log.v(LOG_TAG, "Migrated permission: ${permission.name}, type: " +
+ "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
+ "${permission.protectionLevel}, tree: $isPermissionTree"
+ )
+ }
+ }
+ }
+
+ internal fun migrateUserState(state: AccessState, userId: Int) {
+ val legacyPermissionsManager =
+ LocalServices.getService(PermissionMigrationHelper::class.java)!!
+ val permissionStates = legacyPermissionsManager.getLegacyPermissionStates(userId)
+
+ permissionStates.forEach { (appId, permissionStates) ->
+ migratePermissionStates(appId, state, permissionStates, userId)
+ }
+ }
+
+ private fun migratePermissionStates(
+ appId: Int,
+ state: AccessState,
+ legacyPermissionStates: Map<String, PermissionMigrationHelper.LegacyPermissionState>,
+ userId: Int
+ ) {
+ val permissionFlags =
+ state.userStates[userId].uidPermissionFlags.getOrPut(appId) { IndexedMap() }
+
+ legacyPermissionStates.forEach forEachPermission@ { (permissionName, permissionState) ->
+ val permission = state.systemState.permissions[permissionName]
+ ?: return@forEachPermission
+
+ var flags = when {
+ permission.isNormal -> if (permissionState.isGranted) {
+ PermissionFlags.INSTALL_GRANTED
+ } else {
+ PermissionFlags.INSTALL_REVOKED
+ }
+ permission.isSignature || permission.isInternal ->
+ if (permissionState.isGranted) {
+ if (permission.isDevelopment || permission.isRole) {
+ PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ } else {
+ PermissionFlags.PROTECTION_GRANTED
+ }
+ } else {
+ 0
+ }
+ permission.isRuntime ->
+ if (permissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
+ else -> 0
+ }
+ flags = PermissionFlags.updateFlags(
+ permission, flags, permissionState.flags, permissionState.flags
+ )
+ permissionFlags[permissionName] = flags
+
+ if (DEBUG_MIGRATION) {
+ val oldFlagString = PermissionFlags.apiFlagsToString(permissionState.flags)
+ val newFlagString = PermissionFlags.toString(flags)
+ val oldGrantState = permissionState.isGranted
+ val newGrantState = PermissionFlags.isPermissionGranted(flags)
+ val flagsMismatch = permissionState.flags != PermissionFlags.toApiFlags(flags)
+ Log.v(
+ LOG_TAG, "Migrated appId: $appId, permission: " +
+ "${permission.name}, user: $userId, oldGrantState: $oldGrantState" +
+ ", oldFlags: $oldFlagString, newFlags: $newFlagString, grantMismatch: " +
+ "${oldGrantState != newGrantState}, flagsMismatch: $flagsMismatch"
+ )
+ }
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = UidPermissionMigration::class.java.simpleName
+
+ private const val DEBUG_MIGRATION = false
+ }
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 5a7b37a..132b90d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -48,6 +48,8 @@
class UidPermissionPolicy : SchemePolicy() {
private val persistence = UidPermissionPersistence()
+ private val migration = UidPermissionMigration()
+
@Volatile
private var onPermissionFlagsChangedListeners =
IndexedListSet<OnPermissionFlagsChangedListener>()
@@ -209,16 +211,15 @@
appId: Int,
userId: Int
) {
- resetRuntimePermissions(packageName, appId, userId)
+ resetRuntimePermissions(packageName, userId)
}
- fun MutateStateScope.resetRuntimePermissions(
- packageName: String,
- appId: Int,
- userId: Int
- ) {
- val androidPackage = newState.systemState.packageStates[packageName]?.androidPackage
- ?: return
+ fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) {
+ // It's okay to skip resetting permissions for packages that are removed,
+ // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
+ val packageState = newState.systemState.packageStates[packageName] ?: return
+ val androidPackage = packageState.androidPackage ?: return
+ val appId = packageState.appId
androidPackage.requestedPermissions.forEachIndexed { _, permissionName ->
val permission = newState.systemState.permissions[permissionName]
?: return@forEachIndexed
@@ -446,7 +447,7 @@
return@forEachIndexed
}
} else {
- if (oldPermission != null) {
+ if (oldPermission != null && oldPermission.isReconciled) {
val isPermissionGroupChanged = newPermissionInfo.isRuntime &&
newPermissionInfo.group != null &&
newPermissionInfo.group != oldPermission.groupName
@@ -628,6 +629,8 @@
!oldIsRequestLegacyExternalStorage && newIsRequestLegacyExternalStorage
if ((isNewlyRequestingLegacyExternalStorage || isTargetSdkVersionDowngraded) &&
oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED)) {
+ Log.v(LOG_TAG, "Revoking storage permission: $permissionName for appId: " +
+ " $appId and user: $userId")
val newFlags = oldFlags andInv (
PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK
)
@@ -1385,6 +1388,14 @@
}
}
+ override fun migrateSystemState(state: AccessState) {
+ migration.migrateSystemState(state)
+ }
+
+ override fun migrateUserState(state: AccessState, userId: Int) {
+ migration.migrateUserState(state, userId)
+ }
+
companion object {
private val LOG_TAG = UidPermissionPolicy::class.java.simpleName
diff --git a/services/permission/java/com/android/server/permission/access/util/IntExtensions.kt b/services/permission/java/com/android/server/permission/access/util/IntExtensions.kt
index e71d7a1..bc3328c 100644
--- a/services/permission/java/com/android/server/permission/access/util/IntExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/IntExtensions.kt
@@ -21,3 +21,19 @@
fun Int.hasBits(bits: Int): Boolean = this and bits == bits
infix fun Int.andInv(other: Int): Int = this and other.inv()
+
+inline fun Int.flagsToString(flagToString: (Int) -> String): String {
+ var flags = this
+ return buildString {
+ append("[")
+ while (flags != 0) {
+ val flag = 1 shl flags.countTrailingZeroBits()
+ flags = flags andInv flag
+ append(flagToString(flag))
+ if (flags != 0) {
+ append('|')
+ }
+ }
+ append("]")
+ }
+}
diff --git a/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
new file mode 100644
index 0000000..0ad4184
--- /dev/null
+++ b/services/robotests/src/com/android/server/media/AudioPoliciesBluetoothRouteControllerTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.Application;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaRoute2Info;
+import android.os.UserHandle;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowBluetoothAdapter;
+import org.robolectric.shadows.ShadowBluetoothDevice;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(RobolectricTestRunner.class)
+public class AudioPoliciesBluetoothRouteControllerTest {
+
+ private static final String DEVICE_ADDRESS_UNKNOWN = ":unknown:ip:address:";
+ private static final String DEVICE_ADDRESS_SAMPLE_1 = "30:59:8B:E4:C6:35";
+ private static final String DEVICE_ADDRESS_SAMPLE_2 = "0D:0D:A6:FF:8D:B6";
+ private static final String DEVICE_ADDRESS_SAMPLE_3 = "2D:9B:0C:C2:6F:78";
+ private static final String DEVICE_ADDRESS_SAMPLE_4 = "66:88:F9:2D:A8:1E";
+
+ private Context mContext;
+
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Mock
+ private BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
+
+ @Mock
+ private BluetoothProfileMonitor mBluetoothProfileMonitor;
+
+ private AudioPoliciesBluetoothRouteController mAudioPoliciesBluetoothRouteController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Application application = ApplicationProvider.getApplicationContext();
+ mContext = application;
+
+ BluetoothManager bluetoothManager = (BluetoothManager)
+ mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+
+ BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
+ mShadowBluetoothAdapter = Shadows.shadowOf(bluetoothAdapter);
+
+ mAudioPoliciesBluetoothRouteController =
+ new AudioPoliciesBluetoothRouteController(mContext, bluetoothAdapter,
+ mBluetoothProfileMonitor, mListener) {
+ @Override
+ boolean isDeviceConnected(BluetoothDevice device) {
+ return true;
+ }
+ };
+
+ // Enable A2DP profile.
+ when(mBluetoothProfileMonitor.isProfileSupported(eq(BluetoothProfile.A2DP), any()))
+ .thenReturn(true);
+ mShadowBluetoothAdapter.setProfileConnectionState(BluetoothProfile.A2DP,
+ BluetoothProfile.STATE_CONNECTED);
+
+ mAudioPoliciesBluetoothRouteController.start(UserHandle.of(0));
+ }
+
+ @Test
+ public void getSelectedRoute_noBluetoothRoutesAvailable_returnsNull() {
+ assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
+ }
+
+ @Test
+ public void selectRoute_noBluetoothRoutesAvailable_returnsFalse() {
+ assertThat(mAudioPoliciesBluetoothRouteController
+ .selectRoute(DEVICE_ADDRESS_UNKNOWN)).isFalse();
+ }
+
+ @Test
+ public void selectRoute_noDeviceWithGivenAddress_returnsFalse() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_3);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ assertThat(mAudioPoliciesBluetoothRouteController
+ .selectRoute(DEVICE_ADDRESS_SAMPLE_2)).isFalse();
+ }
+
+ @Test
+ public void selectRoute_deviceIsInDevicesSet_returnsTrue() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ assertThat(mAudioPoliciesBluetoothRouteController
+ .selectRoute(DEVICE_ADDRESS_SAMPLE_1)).isTrue();
+ }
+
+ @Test
+ public void selectRoute_resetSelectedDevice_returnsTrue() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_1);
+ assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
+ }
+
+ @Test
+ public void selectRoute_noSelectedDevice_returnsTrue() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ assertThat(mAudioPoliciesBluetoothRouteController.selectRoute(null)).isTrue();
+ }
+
+ @Test
+ public void getSelectedRoute_updateRouteFailed_returnsNull() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+ mAudioPoliciesBluetoothRouteController
+ .selectRoute(DEVICE_ADDRESS_SAMPLE_3);
+
+ assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
+ }
+
+ @Test
+ public void getSelectedRoute_updateRouteSuccessful_returnsUpdateDevice() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
+
+ assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute()).isNull();
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ assertThat(mAudioPoliciesBluetoothRouteController
+ .selectRoute(DEVICE_ADDRESS_SAMPLE_4)).isTrue();
+
+ MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
+ assertThat(selectedRoute.getAddress()).isEqualTo(DEVICE_ADDRESS_SAMPLE_4);
+ }
+
+ @Test
+ public void getSelectedRoute_resetSelectedRoute_returnsNull() {
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(
+ DEVICE_ADDRESS_SAMPLE_1, DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4);
+
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ // Device is not null now.
+ mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
+ // Rest the device.
+ mAudioPoliciesBluetoothRouteController.selectRoute(null);
+
+ assertThat(mAudioPoliciesBluetoothRouteController.getSelectedRoute())
+ .isNull();
+ }
+
+ @Test
+ public void getTransferableRoutes_noSelectedRoute_returnsAllBluetoothDevices() {
+ String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
+ DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ // Force route controller to update bluetooth devices list.
+ sendBluetoothDevicesChangedBroadcast();
+
+ Set<String> transferableDevices = extractAddressesListFrom(
+ mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
+ assertThat(transferableDevices).containsExactlyElementsIn(addresses);
+ }
+
+ @Test
+ public void getTransferableRoutes_hasSelectedRoute_returnsRoutesWithoutSelectedDevice() {
+ String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
+ DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ // Force route controller to update bluetooth devices list.
+ sendBluetoothDevicesChangedBroadcast();
+ mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
+
+ Set<String> transferableDevices = extractAddressesListFrom(
+ mAudioPoliciesBluetoothRouteController.getTransferableRoutes());
+ assertThat(transferableDevices).containsExactly(DEVICE_ADDRESS_SAMPLE_1,
+ DEVICE_ADDRESS_SAMPLE_2);
+ }
+
+ @Test
+ public void getAllBluetoothRoutes_hasSelectedRoute_returnsAllRoutes() {
+ String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
+ DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ // Force route controller to update bluetooth devices list.
+ sendBluetoothDevicesChangedBroadcast();
+ mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
+
+ Set<String> bluetoothDevices = extractAddressesListFrom(
+ mAudioPoliciesBluetoothRouteController.getAllBluetoothRoutes());
+ assertThat(bluetoothDevices).containsExactlyElementsIn(addresses);
+ }
+
+ @Test
+ public void updateVolumeForDevice_setVolumeForA2DPTo25_selectedRouteVolumeIsUpdated() {
+ String[] addresses = new String[] { DEVICE_ADDRESS_SAMPLE_1,
+ DEVICE_ADDRESS_SAMPLE_2, DEVICE_ADDRESS_SAMPLE_4 };
+ Set<BluetoothDevice> devices = generateFakeBluetoothDevicesSet(addresses);
+ mShadowBluetoothAdapter.setBondedDevices(devices);
+
+ // Force route controller to update bluetooth devices list.
+ sendBluetoothDevicesChangedBroadcast();
+ mAudioPoliciesBluetoothRouteController.selectRoute(DEVICE_ADDRESS_SAMPLE_4);
+
+ mAudioPoliciesBluetoothRouteController.updateVolumeForDevices(
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, 25);
+
+ MediaRoute2Info selectedRoute = mAudioPoliciesBluetoothRouteController.getSelectedRoute();
+ assertThat(selectedRoute.getVolume()).isEqualTo(25);
+ }
+
+ private void sendBluetoothDevicesChangedBroadcast() {
+ Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
+ mContext.sendBroadcast(intent);
+ }
+
+ private static Set<String> extractAddressesListFrom(Collection<MediaRoute2Info> routes) {
+ Set<String> addresses = new HashSet<>();
+
+ for (MediaRoute2Info route: routes) {
+ addresses.add(route.getAddress());
+ }
+
+ return addresses;
+ }
+
+ private static Set<BluetoothDevice> generateFakeBluetoothDevicesSet(String... addresses) {
+ Set<BluetoothDevice> devices = new HashSet<>();
+
+ for (String address: addresses) {
+ devices.add(ShadowBluetoothDevice.newInstance(address));
+ }
+
+ return devices;
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index e711cab..c76af47 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -43,7 +43,6 @@
"ShortcutManagerTestUtils",
"truth-prebuilt",
"testables",
- "ub-uiautomator",
"platformprotosnano",
"framework-protos",
"hamcrest-library",
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 7909ba4..d5cd6ef9 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -224,7 +224,6 @@
MockitoAnnotations.initMocks(this);
when(mSnapshot.getPackageStates()).thenAnswer(x -> mExisting);
- when(mSnapshot.getAllSharedUsers()).thenReturn(mSharedUserSettings);
when(mSnapshot.getUserInfos()).thenReturn(USER_INFO_LIST);
when(mSnapshot.getSharedUser(anyInt())).thenAnswer(invocation -> {
final int sharedUserAppId = invocation.getArgument(0);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index ec9e5b5..1fbb8dd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -19,7 +19,6 @@
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction;
import static com.google.common.truth.Truth.assertThat;
@@ -155,12 +154,6 @@
synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
CachedAppOptimizer.DEFAULT_USE_COMPACTION);
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
@@ -210,12 +203,6 @@
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_USE_COMPACTION, "true", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_1,
- Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_2,
- Integer.toString((CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_THROTTLE_1,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -266,12 +253,6 @@
assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
- .isEqualTo(compactActionIntToAction(
- (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
- .isEqualTo(compactActionIntToAction(
- (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
@@ -404,72 +385,6 @@
}
@Test
- public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
- mCachedAppOptimizerUnderTest.init();
-
- // When we override new values for the compaction action with reasonable values...
-
- // There are four possible values for compactAction[Some|Full].
- for (int i = 1; i < 5; i++) {
- mCountDown = new CountDownLatch(2);
- int expectedSome = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false);
- int expectedFull = (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1;
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- // Then the updates are reflected in the flags.
- synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
- .isEqualTo(compactActionIntToAction(expectedSome));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
- .isEqualTo(compactActionIntToAction(expectedFull));
- }
- }
- }
-
- @Test
- public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
- mCachedAppOptimizerUnderTest.init();
-
- // When we override new values for the compaction action with bad values ...
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_1, "foo", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
- // Then the default values are reflected in the flag
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
- }
-
- mCountDown = new CountDownLatch(2);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_1, "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
- assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
-
- synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
- assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
- .isEqualTo(
- compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
- }
- }
-
- @Test
public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException {
mCachedAppOptimizerUnderTest.init();
@@ -1108,14 +1023,17 @@
mCachedAppOptimizerUnderTest.mLastCompactionStats.clear();
- // We force a some compaction
- mCachedAppOptimizerUnderTest.compactApp(processRecord,
- CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP, true);
- waitForHandler();
- // then process is compacted.
- CachedAppOptimizer.CompactProfile executedCompactProfile =
- processRecord.mOptRecord.getLastCompactProfile();
- assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
+ if (CachedAppOptimizer.ENABLE_FILE_COMPACT) {
+ // We force a some compaction
+ mCachedAppOptimizerUnderTest.compactApp(processRecord,
+ CachedAppOptimizer.CompactProfile.SOME, CachedAppOptimizer.CompactSource.APP,
+ true);
+ waitForHandler();
+ // then process is compacted.
+ CachedAppOptimizer.CompactProfile executedCompactProfile =
+ processRecord.mOptRecord.getLastCompactProfile();
+ assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
+ }
}
private void setFlag(String key, String value, boolean defaultValue) throws Exception {
@@ -1192,7 +1110,7 @@
}
@Override
- public void performCompaction(CachedAppOptimizer.CompactAction action, int pid)
+ public void performCompaction(CachedAppOptimizer.CompactProfile profile, int pid)
throws IOException {
mRss = mRssAfterCompaction;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
index b214787..04f6f8b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java
@@ -92,6 +92,7 @@
expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
/* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
+ /* normalizedAvailableCpuFreqKHz= */ 2_402_267,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
/* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
/* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
@@ -101,6 +102,7 @@
expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
/* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
+ /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
/* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
/* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
@@ -111,6 +113,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
+ /* normalizedAvailableCpuFreqKHz= */ 1_901_608,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
/* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
/* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
@@ -121,6 +124,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
+ /* normalizedAvailableCpuFreqKHz= */ 1_907_125,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
/* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
/* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
@@ -139,6 +143,7 @@
expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 419_354,
+ /* normalizedAvailableCpuFreqKHz= */ 2_425_919,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
/* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
@@ -148,6 +153,7 @@
expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
/* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 429_032,
+ /* normalizedAvailableCpuFreqKHz= */ 2_403_009,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
/* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
@@ -158,6 +164,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 403_225,
+ /* normalizedAvailableCpuFreqKHz= */ 1_688_209,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
/* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
/* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
@@ -168,6 +175,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ false, /* curCpuFreqKHz= */ MISSING_FREQUENCY,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ MISSING_FREQUENCY,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
/* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
@@ -189,6 +197,7 @@
expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
/* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 2_253_713,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
/* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
/* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
@@ -198,6 +207,7 @@
expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
/* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 2_492_687,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
/* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
/* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
@@ -208,6 +218,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 1_788_079,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
/* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
/* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
@@ -218,6 +229,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 1_799_962,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
/* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
/* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
@@ -237,6 +249,7 @@
expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
/* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 2323347,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
/* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
@@ -246,6 +259,7 @@
expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
/* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 209111,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
/* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
@@ -256,6 +270,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 453514,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
/* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
/* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
@@ -266,6 +281,7 @@
FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
/* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
/* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
+ /* normalizedAvailableCpuFreqKHz= */ 37728,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
/* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
/* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
@@ -323,38 +339,8 @@
SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
- expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
- FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 3_000_000,
- /* maxCpuFreqKHz= */ 1_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
- /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
- /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
- /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
- /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)));
- expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
- /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
- /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
- /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
- /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
- /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)));
- expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
- FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
- /* isOnline= */ true, /* curCpuFreqKHz= */ 9, /* maxCpuFreqKHz= */ 2,
- /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
- new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
- /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
- /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
- /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
- /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
- /* guestNiceTimeMillis= */ 0)));
- compareCpuInfos("CPU infos with corrupted CPU frequency", expectedCpuInfos,
- actualCpuInfos);
+ compareCpuInfos("CPU infos with corrupted CPU frequency", expectedCpuInfos, actualCpuInfos);
}
@Test
@@ -368,6 +354,7 @@
expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
/* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
+ /* normalizedAvailableCpuFreqKHz= */ 2_402_267,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
/* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
/* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
@@ -377,6 +364,7 @@
expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
/* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
+ /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
/* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
/* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
@@ -393,7 +381,7 @@
assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
CpuInfoReader cpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile(
VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
- getCacheFile(VALID_PROC_STAT));
+ getCacheFile(VALID_PROC_STAT), /* minReadIntervalMillis= */0);
assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
@@ -406,7 +394,7 @@
File emptyDir = getCacheFile(EMPTY_DIR);
assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir,
- getCacheFile(VALID_PROC_STAT));
+ getCacheFile(VALID_PROC_STAT), /* minReadIntervalMillis= */0);
assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
@@ -420,12 +408,32 @@
assertWithMessage("Create empty file %s", emptyFile).that(emptyFile.createNewFile())
.isTrue();
CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
- getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(EMPTY_FILE));
+ getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(EMPTY_FILE),
+ /* minReadIntervalMillis= */0);
assertWithMessage("Cpu infos with empty proc stat").that(cpuInfoReader.readCpuInfos())
.isNull();
}
+ @Test
+ public void testReadingTooFrequentlyReturnsLastReadCpuInfos() throws Exception {
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
+ getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT),
+ /* minReadIntervalMillis= */ 60_000);
+ assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue();
+
+ SparseArray<CpuInfoReader.CpuInfo> firstCpuInfos = cpuInfoReader.readCpuInfos();
+ assertWithMessage("CPU infos first snapshot").that(firstCpuInfos).isNotNull();
+ assertWithMessage("CPU infos first snapshot size").that(firstCpuInfos.size())
+ .isGreaterThan(0);
+
+ SparseArray<CpuInfoReader.CpuInfo> secondCpuInfos = cpuInfoReader.readCpuInfos();
+ compareCpuInfos("CPU infos second snapshot", firstCpuInfos, secondCpuInfos);
+
+ SparseArray<CpuInfoReader.CpuInfo> thirdCpuInfos = cpuInfoReader.readCpuInfos();
+ compareCpuInfos("CPU infos third snapshot", firstCpuInfos, thirdCpuInfos);
+ }
+
private void compareCpuInfos(String message,
SparseArray<CpuInfoReader.CpuInfo> expected,
SparseArray<CpuInfoReader.CpuInfo> actual) {
@@ -462,7 +470,8 @@
private static CpuInfoReader newCpuInfoReader(File cpusetDir, File cpuFreqDir,
File procStatFile) {
- CpuInfoReader cpuInfoReader = new CpuInfoReader(cpusetDir, cpuFreqDir, procStatFile);
+ CpuInfoReader cpuInfoReader = new CpuInfoReader(cpusetDir, cpuFreqDir, procStatFile,
+ /* minReadIntervalMillis= */ 0);
assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue();
return cpuInfoReader;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 96b6345..3e0e5a8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -64,6 +64,7 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.testutils.OffsettableClock;
@@ -240,12 +241,15 @@
boolean isEnabled) {
DisplayInfo info = new DisplayInfo();
DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+ deviceInfo.uniqueId = uniqueId;
when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock);
when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
+ when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn(
+ DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID);
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
@@ -626,6 +630,19 @@
.setLightSensorEnabled(false);
}
+ @Test
+ public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() {
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
+
+ mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.screenOffBrightnessSensorController).stop();
+ }
+
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId) {
return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true);
@@ -662,8 +679,8 @@
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
- return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
- automaticBrightnessController, wakelockController,
+ return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
+ animator, automaticBrightnessController, wakelockController,
screenOffBrightnessSensorController, hbmMetadata);
}
@@ -673,6 +690,7 @@
*/
private static class DisplayPowerControllerHolder {
public final DisplayPowerController2 dpc;
+ public final LogicalDisplay display;
public final DisplayPowerState displayPowerState;
public final BrightnessSetting brightnessSetting;
public final DualRampAnimator<DisplayPowerState> animator;
@@ -681,7 +699,7 @@
public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
public final HighBrightnessModeMetadata hbmMetadata;
- DisplayPowerControllerHolder(DisplayPowerController2 dpc,
+ DisplayPowerControllerHolder(DisplayPowerController2 dpc, LogicalDisplay display,
DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
DualRampAnimator<DisplayPowerState> animator,
AutomaticBrightnessController automaticBrightnessController,
@@ -689,6 +707,7 @@
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeMetadata hbmMetadata) {
this.dpc = dpc;
+ this.display = display;
this.displayPowerState = displayPowerState;
this.brightnessSetting = brightnessSetting;
this.animator = animator;
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 28319ac..6c4afd3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -64,6 +64,7 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.display.layout.Layout;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.testutils.OffsettableClock;
@@ -243,12 +244,15 @@
boolean isEnabled) {
DisplayInfo info = new DisplayInfo();
DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+ deviceInfo.uniqueId = uniqueId;
when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock);
when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
+ when(logicalDisplayMock.getBrightnessThrottlingDataIdLocked()).thenReturn(
+ DisplayDeviceConfig.DEFAULT_BRIGHTNESS_THROTTLING_DATA_ID);
when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock);
@@ -630,6 +634,19 @@
.setLightSensorEnabled(false);
}
+ @Test
+ public void testStopScreenOffBrightnessSensorControllerWhenDisplayDeviceChanges() {
+ setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+ mock(DisplayDeviceConfig.class), /* isEnabled= */ true);
+
+ mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ verify(mHolder.screenOffBrightnessSensorController).stop();
+ }
+
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId) {
return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true);
@@ -665,8 +682,9 @@
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
- return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
- automaticBrightnessController, screenOffBrightnessSensorController, hbmMetadata);
+ return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
+ animator, automaticBrightnessController, screenOffBrightnessSensorController,
+ hbmMetadata);
}
/**
@@ -675,6 +693,7 @@
*/
private static class DisplayPowerControllerHolder {
public final DisplayPowerController dpc;
+ public final LogicalDisplay display;
public final DisplayPowerState displayPowerState;
public final BrightnessSetting brightnessSetting;
public final DualRampAnimator<DisplayPowerState> animator;
@@ -682,13 +701,14 @@
public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
public final HighBrightnessModeMetadata hbmMetadata;
- DisplayPowerControllerHolder(DisplayPowerController dpc,
+ DisplayPowerControllerHolder(DisplayPowerController dpc, LogicalDisplay display,
DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
DualRampAnimator<DisplayPowerState> animator,
AutomaticBrightnessController automaticBrightnessController,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
HighBrightnessModeMetadata hbmMetadata) {
this.dpc = dpc;
+ this.display = display;
this.displayPowerState = displayPowerState;
this.brightnessSetting = brightnessSetting;
this.animator = animator;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
new file mode 100644
index 0000000..fd9dfe8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.gnss;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.location.GnssMeasurementRequest;
+import android.location.IGnssMeasurementsListener;
+import android.location.LocationManager;
+import android.location.LocationManagerInternal;
+import android.location.util.identity.CallerIdentity;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.FakeUserInfoHelper;
+import com.android.server.location.injector.Injector;
+import com.android.server.location.injector.TestInjector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssMeasurementsProviderTest {
+ private static final int CURRENT_USER = FakeUserInfoHelper.DEFAULT_USERID;
+ private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1000,
+ "mypackage", "attribution", "listener");
+ private static final GnssConfiguration.HalInterfaceVersion HIDL_V2_1 =
+ new GnssConfiguration.HalInterfaceVersion(
+ 2, 1);
+ private static final GnssConfiguration.HalInterfaceVersion AIDL_V3 =
+ new GnssConfiguration.HalInterfaceVersion(
+ GnssConfiguration.HalInterfaceVersion.AIDL_INTERFACE, 3);
+ private static final GnssMeasurementRequest ACTIVE_REQUEST =
+ new GnssMeasurementRequest.Builder().build();
+ private static final GnssMeasurementRequest PASSIVE_REQUEST =
+ new GnssMeasurementRequest.Builder().setIntervalMillis(
+ GnssMeasurementRequest.PASSIVE_INTERVAL).build();
+ private @Mock Context mContext;
+ private @Mock LocationManagerInternal mInternal;
+ private @Mock GnssConfiguration mMockConfiguration;
+ private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
+ private @Mock IGnssMeasurementsListener mListener1;
+ private @Mock IGnssMeasurementsListener mListener2;
+ private @Mock IBinder mBinder1;
+ private @Mock IBinder mBinder2;
+
+ private GnssNative mGnssNative;
+
+ private GnssMeasurementsProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(mBinder1).when(mListener1).asBinder();
+ doReturn(mBinder2).when(mListener2).asBinder();
+ doReturn(true).when(mInternal).isProviderEnabledForUser(eq(LocationManager.GPS_PROVIDER),
+ anyInt());
+ LocalServices.addService(LocationManagerInternal.class, mInternal);
+ FakeGnssHal fakeGnssHal = new FakeGnssHal();
+ GnssNative.setGnssHalForTest(fakeGnssHal);
+ Injector injector = new TestInjector(mContext);
+ mGnssNative = spy(Objects.requireNonNull(
+ GnssNative.create(injector, mMockConfiguration)));
+ mGnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
+ mTestProvider = new GnssMeasurementsProvider(injector, mGnssNative);
+ mGnssNative.register();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(LocationManagerInternal.class);
+ }
+
+ @Test
+ public void testAddListener_active() {
+ // add the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testAddListener_passive() {
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testReregister_aidlV3Plus() {
+ doReturn(AIDL_V3).when(mMockConfiguration).getHalInterfaceVersion();
+
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // add the active request, reregister with the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2);
+ verify(mGnssNative, never()).stopMeasurementCollection();
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request, reregister with the passive request
+ mTestProvider.removeListener(mListener2);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(2)).stopMeasurementCollection();
+ }
+
+ @Test
+ public void testReregister_preAidlV3() {
+ doReturn(HIDL_V2_1).when(mMockConfiguration).getHalInterfaceVersion();
+
+ // add the passive request
+ mTestProvider.addListener(PASSIVE_REQUEST, IDENTITY, mListener1);
+ verify(mGnssNative, never()).startMeasurementCollection(anyBoolean(), anyBoolean(),
+ anyInt());
+
+ // add the active request, reregister with the active request
+ mTestProvider.addListener(ACTIVE_REQUEST, IDENTITY, mListener2);
+ verify(mGnssNative, times(1)).stopMeasurementCollection();
+ verify(mGnssNative, times(1)).startMeasurementCollection(
+ eq(ACTIVE_REQUEST.isFullTracking()),
+ eq(ACTIVE_REQUEST.isCorrelationVectorOutputsEnabled()),
+ eq(ACTIVE_REQUEST.getIntervalMillis()));
+
+ // remove the active request, reregister with the passive request
+ mTestProvider.removeListener(mListener2);
+ verify(mGnssNative, times(2)).stopMeasurementCollection();
+
+ // remove the passive request
+ mTestProvider.removeListener(mListener1);
+ verify(mGnssNative, times(3)).stopMeasurementCollection();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java
index 2cf57da..7ee411b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java
@@ -27,6 +27,7 @@
public void setInEmergency(boolean inEmergency) {
mInEmergency = inEmergency;
+ dispatchEmergencyStateChanged();
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 7dc1935..293003d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -80,8 +80,12 @@
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.util.Log;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -174,6 +178,8 @@
doReturn(mResources).when(mContext).getResources();
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+ doReturn(ApplicationProvider.getApplicationContext()).when(
+ mContext).getApplicationContext();
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
doReturn(PackageManager.PERMISSION_DENIED)
.when(mContext)
@@ -210,6 +216,8 @@
@After
public void tearDown() throws Exception {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS,
+ DeviceConfig.NAMESPACE_LOCATION);
LocalServices.removeServiceForTest(LocationManagerInternal.class);
// some test failures may leave the fg thread stuck, interrupt until we get out of it
@@ -1339,6 +1347,144 @@
assertThat(mManager.isVisibleToCaller()).isFalse();
}
+ @MediumTest
+ @Test
+ public void testEnableMsl_expectedBehavior() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", Boolean.toString(true), false);
+
+ // Create a random location and set provider location to cache necessary MSL assets.
+ Location loc = createLocation(NAME, mRandom);
+ loc.setAltitude(mRandom.nextDouble());
+ loc.setVerticalAccuracyMeters(mRandom.nextFloat());
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ Thread.sleep(1000);
+
+ // Register listener and reset provider location to capture.
+ ILocationListener listener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+ mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
+ verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));
+
+ // Assert that MSL fields are populated.
+ Location actual = captor.getValue().get(0);
+ assertThat(actual.hasMslAltitude()).isTrue();
+ assertThat(actual.hasMslAltitudeAccuracy()).isTrue();
+ }
+
+ @MediumTest
+ @Test
+ public void testEnableMsl_noVerticalAccuracy() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", Boolean.toString(true), false);
+
+ // Create a random location and set provider location to cache necessary MSL assets.
+ Location loc = createLocation(NAME, mRandom);
+ loc.setAltitude(mRandom.nextDouble());
+ loc.setVerticalAccuracyMeters(mRandom.nextFloat());
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ Thread.sleep(1000);
+
+ // Register listener and reset provider location with no vertical accuracy to capture.
+ ILocationListener listener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+ mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+ loc.removeVerticalAccuracy();
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
+ verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));
+
+ // Assert that only the MSL accuracy field is populated.
+ Location actual = captor.getValue().get(0);
+ assertThat(actual.hasMslAltitude()).isTrue();
+ assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ public void testEnableMsl_noAltitude() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", Boolean.toString(true), false);
+
+ // Create a random location and set provider location to cache necessary MSL assets.
+ Location loc = createLocation(NAME, mRandom);
+ loc.setAltitude(mRandom.nextDouble());
+ loc.setVerticalAccuracyMeters(mRandom.nextFloat());
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ Thread.sleep(1000);
+
+ // Register listener and reset provider location with no altitude to capture.
+ ILocationListener listener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+ mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+ loc.removeAltitude();
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
+ verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));
+
+ // Assert that no MSL fields are populated.
+ Location actual = captor.getValue().get(0);
+ assertThat(actual.hasMslAltitude()).isFalse();
+ assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ public void testEnableMsl_invalidAltitude() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", Boolean.toString(true), false);
+
+ // Create a random location and set provider location to cache necessary MSL assets.
+ Location loc = createLocation(NAME, mRandom);
+ loc.setAltitude(mRandom.nextDouble());
+ loc.setVerticalAccuracyMeters(mRandom.nextFloat());
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ Thread.sleep(1000);
+
+ // Register listener and reset provider location with invalid altitude to capture.
+ ILocationListener listener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+ mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+ loc.setAltitude(Double.POSITIVE_INFINITY);
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
+ verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));
+
+ // Assert that no MSL fields are populated.
+ Location actual = captor.getValue().get(0);
+ assertThat(actual.hasMslAltitude()).isFalse();
+ assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ public void testDisableMsl_expectedBehavior() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_LOCATION,
+ "enable_location_provider_manager_msl", Boolean.toString(false), false);
+
+ // Create a random location and set provider location to cache necessary MSL assets.
+ Location loc = createLocation(NAME, mRandom);
+ loc.setAltitude(mRandom.nextDouble());
+ loc.setVerticalAccuracyMeters(mRandom.nextFloat());
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ Thread.sleep(1000);
+
+ // Register listener and reset provider location to capture.
+ ILocationListener listener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+ mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
+ mProvider.setProviderLocation(LocationResult.wrap(loc));
+ ArgumentCaptor<List<Location>> captor = ArgumentCaptor.forClass(List.class);
+ verify(listener).onLocationChanged(captor.capture(), nullable(IRemoteCallback.class));
+
+ // Assert that no MSL fields are populated.
+ Location actual = captor.getValue().get(0);
+ assertThat(actual.hasMslAltitude()).isFalse();
+ assertThat(actual.hasMslAltitudeAccuracy()).isFalse();
+ }
+
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
index 9aa53db..8979585 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
@@ -20,6 +20,8 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
+import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
+import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
import static com.android.server.pm.UserVisibilityChangedEvent.onInvisible;
import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
@@ -73,8 +75,8 @@
assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
// Make sure another user cannot be started on default display
- int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
- BG_VISIBLE, DEFAULT_DISPLAY);
+ int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, otherUserId, BG_VISIBLE,
+ DEFAULT_DISPLAY);
assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
"when user (%d) is starting on default display after it was started by user %d",
otherUserId, visibleBgUserId);
@@ -117,8 +119,8 @@
assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
// Make sure another user cannot be started on default display
- int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
- BG_VISIBLE, DEFAULT_DISPLAY);
+ int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, otherUserId, BG_VISIBLE,
+ DEFAULT_DISPLAY);
assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
"when user (%d) is starting on default display after it was started by user %d",
otherUserId, visibleBgUserId);
@@ -127,8 +129,6 @@
listener.verify();
}
- /* TODO(b/261538337): re-add after the reverted CL is merged again
-
@Test
public void
testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnBgOnSecondaryDisplay()
@@ -226,5 +226,4 @@
listener.verify();
}
- */
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index 1bf921c..2774803 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -44,7 +44,6 @@
import android.util.IntArray;
import android.util.Log;
-import com.android.internal.util.Preconditions;
import com.android.server.DumpableDumperRule;
import com.android.server.ExpectableTestCase;
@@ -152,6 +151,12 @@
}
@Test
+ public final void testAssignUserToDisplayOnStart_invalidUserStartMode() {
+ assertThrows(IllegalArgumentException.class, () -> mMediator
+ .assignUserToDisplayOnStart(USER_ID, USER_ID, 666, DEFAULT_DISPLAY));
+ }
+
+ @Test
public final void testStartFgUser_onSecondaryDisplay() throws Exception {
AsyncUserVisibilityListener listener = addListenerForNoEvents();
@@ -286,7 +291,7 @@
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
BG_VISIBLE, DEFAULT_DISPLAY);
- assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
@@ -302,14 +307,14 @@
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
BG_VISIBLE, DEFAULT_DISPLAY);
- assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
expectNoDisplayAssignedToUser(PROFILE_USER_ID);
expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY);
- assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+ assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
listener.verify();
}
@@ -335,6 +340,41 @@
}
@Test
+ public final void testStartBgProfile_onDefaultDisplay_whenParentIsNotStarted()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForNoEvents();
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
+ DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
+
+ expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
+ expectNoDisplayAssignedToUser(PROFILE_USER_ID);
+
+ listener.verify();
+ }
+
+ @Test
+ public final void testStartBgProfile_onDefaultDisplay_whenParentIsStartedOnBg()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForNoEvents();
+ startBackgroundUser(PARENT_USER_ID);
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
+ DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
+
+ expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
+
+ expectNoDisplayAssignedToUser(PROFILE_USER_ID);
+ expectInitialCurrentUserAssignedToDisplay(DEFAULT_DISPLAY);
+
+ assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
+
+ @Test
public final void testStartBgProfile_onSecondaryDisplay() throws Exception {
AsyncUserVisibilityListener listener = addListenerForNoEvents();
@@ -488,8 +528,6 @@
* se.
*/
protected final void startUserInSecondaryDisplay(@UserIdInt int userId, int displayId) {
- Preconditions.checkArgument(displayId != INVALID_DISPLAY && displayId != DEFAULT_DISPLAY,
- "must pass a secondary display, not %d", displayId);
Log.d(TAG, "startUserInSecondaryDisplay(" + userId + ", " + displayId + ")");
int result = mMediator.assignUserToDisplayOnStart(userId, userId, BG_VISIBLE, displayId);
if (result != USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
index af85ef4..e82910f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
@@ -109,34 +109,6 @@
}
@Test
- public final void testStartVisibleBgProfile_onDefaultDisplay_whenParentIsCurrentUser()
- throws Exception {
- AsyncUserVisibilityListener listener = addListenerForEvents(
- onInvisible(INITIAL_CURRENT_USER_ID),
- onVisible(PARENT_USER_ID),
- onVisible(PROFILE_USER_ID));
- startForegroundUser(PARENT_USER_ID);
-
- int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
- BG_VISIBLE, DEFAULT_DISPLAY);
- assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
- expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY);
-
- expectUserIsVisible(PROFILE_USER_ID);
- expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY);
- expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
- expectUserIsVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY);
- expectVisibleUsers(PARENT_USER_ID, PROFILE_USER_ID);
-
- expectDisplayAssignedToUser(PROFILE_USER_ID, DEFAULT_DISPLAY);
- expectUserAssignedToDisplay(DEFAULT_DISPLAY, PARENT_USER_ID);
-
- assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
-
- listener.verify();
- }
-
- @Test
public final void testStartFgUser_onInvalidDisplay() throws Exception {
AsyncUserVisibilityListener listener = addListenerForNoEvents();
@@ -301,14 +273,83 @@
}
@Test
+ public final void testStartVisibleBgProfile_onDefaultDisplay_whenParentIsCurrentUser()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForEvents(
+ onInvisible(INITIAL_CURRENT_USER_ID),
+ onVisible(PARENT_USER_ID),
+ onVisible(PROFILE_USER_ID));
+ startForegroundUser(PARENT_USER_ID);
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
+ BG_VISIBLE, DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
+ expectUserCannotBeUnassignedFromDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY);
+
+ expectUserIsVisible(PROFILE_USER_ID);
+ expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, INVALID_DISPLAY);
+ expectUserIsNotVisibleOnDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+ expectUserIsVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY);
+ expectVisibleUsers(PARENT_USER_ID, PROFILE_USER_ID);
+
+ expectDisplayAssignedToUser(PROFILE_USER_ID, DEFAULT_DISPLAY);
+ expectUserAssignedToDisplay(DEFAULT_DISPLAY, PARENT_USER_ID);
+
+ assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
+
+ @Test
public final void
- testStartVisibleBgProfile_onDefaultDisplay_whenParentVisibleOnSecondaryDisplay()
- throws Exception {
+ testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnAnotherDisplay()
+ throws Exception {
AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(PARENT_USER_ID));
startUserInSecondaryDisplay(PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
BG_VISIBLE, DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
+
+ expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
+ expectNoDisplayAssignedToUser(PROFILE_USER_ID);
+ expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, PARENT_USER_ID);
+
+ assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
+
+ // Not supported - profiles can only be started on default display
+ @Test
+ public final void
+ testStartVisibleBgProfile_onSecondaryDisplay_whenParentIsStartedVisibleOnThatDisplay()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(PARENT_USER_ID));
+ startUserInSecondaryDisplay(PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
+ BG_VISIBLE, DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
+
+ expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
+ expectNoDisplayAssignedToUser(PROFILE_USER_ID);
+ expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, PARENT_USER_ID);
+
+ assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
+
+ @Test
+ public final void
+ testStartProfile_onDefaultDisplay_whenParentIsStartedVisibleOnSecondaryDisplay()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(PARENT_USER_ID));
+ startUserInSecondaryDisplay(PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID, BG,
+ DEFAULT_DISPLAY);
assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE);
expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 24b003c..caa2e36 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.database.sqlite.SQLiteDatabase.deleteDatabase;
+import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -719,6 +720,41 @@
}
@SmallTest
+ public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags()
+ throws Exception {
+ unlockSystemUser();
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = new ActivityInfo();
+ resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
+ when(mMockPackageManager.resolveActivityAsUser(
+ any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo);
+ when(mMockPackageManager.checkSignatures(
+ anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ Response response = new Response(latch, mMockAccountManagerResponse);
+ Bundle options = createOptionsWithAccountName(
+ AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE);
+ int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
+ options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags);
+
+ mAms.startAddAccountSession(
+ response, // response
+ AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType
+ "authTokenType",
+ null, // requiredFeatures
+ true, // expectActivityLaunch
+ options); // optionsIn
+ waitForLatch(latch);
+
+ verify(mMockAccountManagerResponse).onError(
+ eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent"));
+ }
+
+ @SmallTest
public void testStartAddAccountSessionError() throws Exception {
unlockSystemUser();
Bundle options = createOptionsWithAccountName(
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java
index 73f30d9..b98a6a8 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java
@@ -17,9 +17,6 @@
import android.accounts.Account;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Constants shared between test AccountAuthenticators and AccountManagerServiceTest.
*/
@@ -31,6 +28,8 @@
"account_manager_service_test:account_status_token_key";
public static final String KEY_ACCOUNT_PASSWORD =
"account_manager_service_test:account_password_key";
+ public static final String KEY_INTENT_FLAGS =
+ "account_manager_service_test:intent_flags_key";
public static final String KEY_OPTIONS_BUNDLE =
"account_manager_service_test:option_bundle_key";
public static final String ACCOUNT_NAME_SUCCESS = "[email protected]";
diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java
index 8106364..924443e 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java
@@ -24,8 +24,6 @@
import android.content.Intent;
import android.os.Bundle;
-import com.android.frameworks.servicestests.R;
-
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -270,11 +268,13 @@
String accountName = null;
Bundle sessionBundle = null;
String password = null;
+ int intentFlags = 0;
if (options != null) {
accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME);
sessionBundle = options.getBundle(
AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE);
password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD);
+ intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0);
}
Bundle result = new Bundle();
@@ -302,6 +302,7 @@
intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT,
eventualActivityResultData);
intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response);
+ intent.setFlags(intentFlags);
result.putParcelable(AccountManager.KEY_INTENT, intent);
} else {
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index e68a8a0..01563e2 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -106,6 +106,64 @@
mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
}
+ @Test
+ public void testStrictRepeatedLimiting() throws Exception {
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 minutes there should be nothing left in the buffer and the same type of entry
+ // should not get rate limited anymore.
+ mClock.setOffsetMillis(11 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The first 6 entries should not be rate limited again.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After 11 more minutes there should be nothing left in the buffer and the same type of
+ // entry should not get rate limited anymore.
+ mClock.setOffsetMillis(22 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // Repeated crashes after the last reset being rate limited should be restricted faster.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // We now need to wait 61 minutes for the buffer should be empty again.
+ mClock.setOffsetMillis(83 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // After yet another 61 minutes, this time without triggering rate limiting, the strict
+ // limiting should be turnd off.
+ mClock.setOffsetMillis(144 * 60 * 1000);
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+
+ // As rate limiting was not triggered in the last reset, after another 11 minutes the
+ // buffer should still act as normal.
+ mClock.setOffsetMillis(155 * 60 * 1000);
+ // The first 6 entries should not be rate limited.
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ // The 7th entry of the same process should be rate limited.
+ assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+ }
+
private static class TestClock implements DropboxRateLimiter.Clock {
long mOffsetMillis = 0L;
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 4412cfe..8cbed2c 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -305,60 +305,4 @@
assertEquals("Interaction event time was not updated correctly.",
interactionEventTime, mProcessRecord.mState.getInteractionEventTime());
}
-
- private void updateShortFgsOwner(int uid, int pid, boolean add) {
- sService.mOomAdjuster.updateShortFgsOwner(uid, pid, add);
- }
-
- private void assertHasUidShortForegroundService(int uid, boolean expected) {
- assertEquals(expected, sService.mOomAdjuster.hasUidShortForegroundService(uid));
- }
-
- @Test
- public void testHasUidShortForegroundService() {
- assertHasUidShortForegroundService(1, false);
- assertHasUidShortForegroundService(2, false);
- assertHasUidShortForegroundService(3, false);
- assertHasUidShortForegroundService(100, false);
- assertHasUidShortForegroundService(101, false);
-
- updateShortFgsOwner(1, 100, true);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(100, false);
- assertHasUidShortForegroundService(2, false);
-
- updateShortFgsOwner(1, 101, true);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(101, false);
- assertHasUidShortForegroundService(2, false);
-
- updateShortFgsOwner(2, 200, true);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, true);
- assertHasUidShortForegroundService(200, false);
-
- updateShortFgsOwner(1, 101, false);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, true);
-
- updateShortFgsOwner(1, 99, false); // unused PID
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, true);
-
- updateShortFgsOwner(1, 100, false);
- assertHasUidShortForegroundService(1, false);
- assertHasUidShortForegroundService(2, true);
-
- updateShortFgsOwner(1, 100, true);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, true);
-
- updateShortFgsOwner(2, 200, false);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, false);
-
- updateShortFgsOwner(2, 201, true);
- assertHasUidShortForegroundService(1, true);
- assertHasUidShortForegroundService(2, true);
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
index 47fdcb6..b5229d8 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.appops;
+package com.android.server.appop;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 1b6b143..7971fd7 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -127,6 +128,10 @@
private Context mContext;
+ private int mHdrConversionMode;
+
+ private int mPreferredHdrOutputType;
+
private final DisplayManagerService.Injector mShortMockedInjector =
new DisplayManagerService.Injector() {
@Override
@@ -176,6 +181,8 @@
@Override
int setHdrConversionMode(int conversionMode, int preferredHdrOutputType,
int[] autoHdrTypes) {
+ mHdrConversionMode = conversionMode;
+ mPreferredHdrOutputType = preferredHdrOutputType;
return Display.HdrCapabilities.HDR_TYPE_INVALID;
}
@@ -185,7 +192,7 @@
}
boolean getHdrOutputConversionSupport() {
- return false;
+ return true;
}
}
@@ -1541,6 +1548,40 @@
new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_SYSTEM));
}
+ @Test
+ public void testCreateHdrConversionMode_withInvalidArguments_throwsException() {
+ assertThrows(
+ "preferredHdrOutputType must not be set if the conversion mode is "
+ + "HDR_CONVERSION_PASSTHROUGH",
+ IllegalArgumentException.class,
+ () -> new HdrConversionMode(HdrConversionMode.HDR_CONVERSION_PASSTHROUGH,
+ Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION));
+ }
+
+ @Test
+ public void testSetHdrConversionModeInternal_withInvalidArguments_throwsException() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ assertThrows("Expected DisplayManager to throw IllegalArgumentException when "
+ + "preferredHdrOutputType is set and the conversion mode is "
+ + "HDR_CONVERSION_SYSTEM",
+ IllegalArgumentException.class,
+ () -> displayManager.setHdrConversionModeInternal(new HdrConversionMode(
+ HdrConversionMode.HDR_CONVERSION_SYSTEM,
+ Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION)));
+ }
+
+ @Test
+ public void testSetAndGetHdrConversionModeInternal() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ final HdrConversionMode mode = new HdrConversionMode(
+ HdrConversionMode.HDR_CONVERSION_FORCE,
+ Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION);
+ displayManager.setHdrConversionModeInternal(mode);
+ assertEquals(mode, displayManager.getHdrConversionModeSettingInternal());
+ assertEquals(mode.getConversionMode(), mHdrConversionMode);
+ assertEquals(mode.getPreferredHdrOutputType(), mPreferredHdrOutputType);
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
throws Exception {
DisplayManagerService displayManager =
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 4a2e5d7..9fc46c5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -86,7 +86,6 @@
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.Pair;
import android.util.Range;
import com.android.internal.app.ChooserActivity;
@@ -187,7 +186,6 @@
private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
private TestLooper mLooper;
- private TestPerPackageThrottler mShortcutThrottler;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -277,9 +275,7 @@
mInjector = new TestInjector();
mLooper = new TestLooper();
- mShortcutThrottler = new TestPerPackageThrottler();
- mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper(),
- mShortcutThrottler);
+ mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
mDataManager.initialize();
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
@@ -287,7 +283,10 @@
mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mockGetShortcuts(Collections.singletonList(mShortcutInfo));
+ when(mShortcutServiceInternal.getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ anyInt(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mShortcutInfo));
verify(mShortcutServiceInternal).addShortcutChangeCallback(
mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
@@ -973,7 +972,6 @@
buildPerson());
ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
buildPerson());
- mockGetShortcuts(List.of(shortcut1, shortcut2, shortcut3));
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
@@ -1225,6 +1223,7 @@
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
}
+
@Test
public void testUncacheOldestCachedShortcut_missingNotificationEvents() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1234,7 +1233,6 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mockGetShortcuts(Collections.singletonList(shortcut));
mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME,
Collections.singletonList(shortcut),
@@ -1254,6 +1252,7 @@
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
}
+
@Test
public void testUncacheOldestCachedShortcut_legacyConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1275,7 +1274,6 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mockGetShortcuts(Collections.singletonList(shortcut));
mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME,
Collections.singletonList(shortcut),
@@ -1313,8 +1311,7 @@
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
- DataManager dataManager = new DataManager(
- mContext, mInjector, mLooper.getLooper(), mShortcutThrottler);
+ DataManager dataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
dataManager.onUserUnlocked(USER_ID_PRIMARY);
dataManager.restore(USER_ID_PRIMARY, payload);
ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
@@ -1726,13 +1723,6 @@
return (queryFlags & flag) != 0;
}
- private void mockGetShortcuts(List<ShortcutInfo> shortcutInfoList) {
- when(mShortcutServiceInternal.getShortcuts(
- anyInt(), anyString(), anyLong(), anyString(), any(), any(), any(),
- anyInt(), anyInt(), anyInt(), anyInt()))
- .thenReturn(shortcutInfoList);
- }
-
// "Sends" a notification to a non-customized notification channel - the notification channel
// is something generic like "messages" and the notification has a shortcut id
private void sendGenericNotification() {
@@ -1994,11 +1984,4 @@
return mUsageStatsQueryHelper;
}
}
-
- private static class TestPerPackageThrottler implements PerPackageThrottler {
- @Override
- public void scheduleDebounced(Pair<String, Integer> pkgUserId, Runnable runnable) {
- runnable.run();
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java
deleted file mode 100644
index 672cbb9..0000000
--- a/services/tests/servicestests/src/com/android/server/people/data/PerPackageThrottlerImplTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.people.data;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.util.Pair;
-
-import com.android.server.testutils.OffsettableClock;
-import com.android.server.testutils.TestHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-@RunWith(JUnit4.class)
-public class PerPackageThrottlerImplTest {
- private static final int DEBOUNCE_INTERVAL = 500;
- private static final String PKG_ONE = "pkg_one";
- private static final String PKG_TWO = "pkg_two";
- private static final int USER_ID = 10;
-
- private final OffsettableClock mClock = new OffsettableClock.Stopped();
- private final TestHandler mTestHandler = new TestHandler(null, mClock);
- private PerPackageThrottlerImpl mThrottler;
-
- @Before
- public void setUp() {
- mThrottler = new PerPackageThrottlerImpl(mTestHandler, DEBOUNCE_INTERVAL);
- }
-
- @Test
- public void scheduleDebounced() {
- AtomicBoolean pkgOneRan = new AtomicBoolean();
- AtomicBoolean pkgTwoRan = new AtomicBoolean();
-
- mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
- mThrottler.scheduleDebounced(new Pair<>(PKG_ONE, USER_ID), () -> pkgOneRan.set(true));
- mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
- mThrottler.scheduleDebounced(new Pair<>(PKG_TWO, USER_ID), () -> pkgTwoRan.set(true));
-
- assertFalse(pkgOneRan.get());
- assertFalse(pkgTwoRan.get());
- mClock.fastForward(DEBOUNCE_INTERVAL);
- mTestHandler.timeAdvance();
- assertTrue(pkgOneRan.get());
- assertTrue(pkgTwoRan.get());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java
index 4434a32..32c9e75 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceShellCommandTest.java
@@ -121,4 +121,31 @@
assertEquals("Couldn't get main user.", mOutStream.toString().trim());
}
+ @Test
+ public void testCanSwitchToHeadlessSystemUser() {
+ doReturn(true).when(mUserManagerService).canSwitchToHeadlessSystemUser();
+ doReturn(mWriter).when(mCommand).getOutPrintWriter();
+
+ assertEquals(0, mCommand.exec(mBinder, in, out, err,
+ new String[]{"can-switch-to-headless-system-user"},
+ mShellCallback, mResultReceiver));
+
+ mWriter.flush();
+ assertEquals("true", mOutStream.toString().trim());
+ }
+
+
+ @Test
+ public void testIsMainUserPermanentAdmin() {
+ doReturn(false).when(mUserManagerService).isMainUserPermanentAdmin();
+ doReturn(mWriter).when(mCommand).getOutPrintWriter();
+
+ assertEquals(0, mCommand.exec(mBinder, in, out, err,
+ new String[]{"is-main-user-permanent-admin"}, mShellCallback, mResultReceiver));
+
+ mWriter.flush();
+ assertEquals("false", mOutStream.toString().trim());
+ }
+
+
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index d71deaf..a0fb3de 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -16,7 +16,6 @@
package com.android.server.power;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -30,8 +29,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalMatchers.gt;
-import static org.mockito.AdditionalMatchers.leq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -153,9 +150,6 @@
@Mock
private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
- @Mock
- private ActivityManagerInternal mActivityManagerInternal;
-
private PowerManagerService mService;
private ContextWrapper mContextSpy;
private BatteryReceiver mBatteryReceiver;
@@ -211,7 +205,6 @@
addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
- addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternal);
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mResourcesSpy = spy(mContextSpy.getResources());
@@ -228,14 +221,6 @@
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
-
- // Set up canHoldWakeLocksInDeepDoze.
- // - procstate <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE -> true
- // - procstate > PROCESS_STATE_BOUND_FOREGROUND_SERVICE -> false
- when(mActivityManagerInternal.canHoldWakeLocksInDeepDoze(
- anyInt(), leq(PROCESS_STATE_BOUND_FOREGROUND_SERVICE))).thenReturn(true);
- when(mActivityManagerInternal.canHoldWakeLocksInDeepDoze(
- anyInt(), gt(PROCESS_STATE_BOUND_FOREGROUND_SERVICE))).thenReturn(false);
}
private PowerManagerService createService() {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index d50aca9..843e2b4 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -49,6 +49,7 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
@@ -121,18 +122,14 @@
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
- @Mock
- private PowerManagerInternal mPowerManagerInternalMock;
- @Mock
- private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
- @Mock
- private PackageManagerInternal mPackageManagerInternalMock;
- @Mock
- private VibrationConfig mVibrationConfigMock;
+ @Mock private PowerManagerInternal mPowerManagerInternalMock;
+ @Mock private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
+ @Mock private PackageManagerInternal mPackageManagerInternalMock;
+ @Mock private AudioManager mAudioManagerMock;
+ @Mock private VibrationConfig mVibrationConfigMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
- private AudioManager mAudioManager;
private VibrationSettings mVibrationSettings;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener;
@@ -146,6 +143,7 @@
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+ when(mContextSpy.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManagerMock);
doAnswer(invocation -> {
mRegisteredPowerModeListener = invocation.getArgument(0);
return null;
@@ -165,7 +163,6 @@
addServicesForTest();
setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
- mAudioManager = mContextSpy.getSystemService(AudioManager.class);
mVibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
@@ -211,9 +208,34 @@
}
@Test
+ public void addListener_switchUserTriggerListener() {
+ mVibrationSettings.addListener(mListenerMock);
+
+ // Testing the broadcast flow manually.
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_USER_SWITCHED));
+
+ verify(mListenerMock).onChange();
+ }
+
+ @Test
+ public void addListener_ringerModeChangeTriggerListener() {
+ mVibrationSettings.addListener(mListenerMock);
+
+ // Testing the broadcast flow manually.
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+
+ verify(mListenerMock, times(2)).onChange();
+ }
+
+ @Test
public void addListener_settingsChangeTriggerListener() {
mVibrationSettings.addListener(mListenerMock);
+ // Testing the broadcast flow manually.
mVibrationSettings.mSettingObserver.onChange(false);
mVibrationSettings.mSettingObserver.onChange(false);
@@ -224,6 +246,7 @@
public void addListener_lowPowerModeChangeTriggerListener() {
mVibrationSettings.addListener(mListenerMock);
+ // Testing the broadcast flow manually.
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); // No change.
@@ -235,13 +258,20 @@
public void removeListener_noMoreCallbacksToListener() {
mVibrationSettings.addListener(mListenerMock);
- setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, 0);
+ mVibrationSettings.mSettingObserver.onChange(false);
verify(mListenerMock).onChange();
mVibrationSettings.removeListener(mListenerMock);
+ // Trigger multiple observers manually.
+ mVibrationSettings.mSettingObserver.onChange(false);
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
+ new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+
verifyNoMoreInteractions(mListenerMock);
- setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
}
@Test
@@ -482,7 +512,7 @@
assertVibrationNotIgnoredForUsage(USAGE_RINGTONE);
// Testing the broadcast flow manually.
- mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
+ when(mAudioManagerMock.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
@@ -785,7 +815,6 @@
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
-
private String errorMessageForUsage(int usage) {
return "Error for usage " + VibrationAttributes.usageToString(usage);
}
@@ -814,8 +843,8 @@
}
private void setRingerMode(int ringerMode) {
- mAudioManager.setRingerModeInternal(ringerMode);
- assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
+ when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
+ // Mock AudioManager broadcast of internal ringer mode change.
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 12f124e..6f6e2242 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -38,11 +38,12 @@
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
-import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
-import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
-import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER;
-import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER;
-import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
+import static com.android.os.dnd.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
+import static com.android.os.dnd.DNDModeProto.ENABLED_FIELD_NUMBER;
+import static com.android.os.dnd.DNDModeProto.ID_FIELD_NUMBER;
+import static com.android.os.dnd.DNDModeProto.UID_FIELD_NUMBER;
+import static com.android.os.dnd.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
+import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
import static junit.framework.Assert.assertEquals;
@@ -95,7 +96,6 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
-import android.service.notification.DNDModeProto;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenPolicy;
@@ -898,7 +898,7 @@
assertEquals(n + 1, events.size());
for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
if (builder.getAtomId() == DND_MODE_RULE) {
- if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == DNDModeProto.ROOT_CONFIG) {
+ if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == ROOT_CONFIG) {
assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER));
}
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 d242a5f..cda8dd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -117,6 +117,8 @@
import org.mockito.ArgumentCaptor;
import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Tests for Size Compatibility mode.
@@ -172,6 +174,156 @@
}
@Test
+ public void testHorizontalReachabilityEnabledForTranslucentActivities() {
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
+ config.setTranslucentLetterboxingOverrideEnabled(true);
+ config.setLetterboxHorizontalPositionMultiplier(0.5f);
+ config.setIsHorizontalReachabilityEnabled(true);
+
+ // Opaque activity
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+
+ spyOn(translucentActivity.mLetterboxUiController);
+ doReturn(true).when(translucentActivity.mLetterboxUiController)
+ .shouldShowLetterboxUi(any());
+
+ addWindowToActivity(translucentActivity);
+ translucentActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final Function<ActivityRecord, Rect> innerBoundsOf =
+ (ActivityRecord a) -> {
+ final Rect bounds = new Rect();
+ a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ return bounds;
+ };
+ final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
+ innerBoundsOf.apply(translucentActivity));
+ final Runnable checkIsLeft = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
+ final Runnable checkIsRight = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500);
+ final Runnable checkIsCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left > 0
+ && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue();
+
+ final Consumer<Integer> doubleClick =
+ (Integer x) -> {
+ mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ // Initial state
+ checkIsCentered.run();
+
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsLeft.run();
+
+ // Double-click right
+ doubleClick.accept(/* x */ 1990);
+ checkLetterboxPositions.run();
+ checkIsCentered.run();
+
+ // Double-click right
+ doubleClick.accept(/* x */ 1990);
+ checkLetterboxPositions.run();
+ checkIsRight.run();
+
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsCentered.run();
+ }
+
+ @Test
+ public void testVerticalReachabilityEnabledForTranslucentActivities() {
+ setUpDisplaySizeWithApp(1000, 2500);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
+ config.setTranslucentLetterboxingOverrideEnabled(true);
+ config.setLetterboxVerticalPositionMultiplier(0.5f);
+ config.setIsVerticalReachabilityEnabled(true);
+
+ // Opaque activity
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ // Translucent Activity
+ final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+ .setLaunchedFromUid(mActivity.getUid())
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .build();
+ doReturn(false).when(translucentActivity).fillsParent();
+ mTask.addChild(translucentActivity);
+
+ spyOn(translucentActivity.mLetterboxUiController);
+ doReturn(true).when(translucentActivity.mLetterboxUiController)
+ .shouldShowLetterboxUi(any());
+
+ addWindowToActivity(translucentActivity);
+ translucentActivity.mRootWindowContainer.performSurfacePlacement();
+
+ final Function<ActivityRecord, Rect> innerBoundsOf =
+ (ActivityRecord a) -> {
+ final Rect bounds = new Rect();
+ a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+ return bounds;
+ };
+ final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
+ innerBoundsOf.apply(translucentActivity));
+ final Runnable checkIsTop = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);
+ final Runnable checkIsBottom = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500);
+ final Runnable checkIsCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).top > 0
+ && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue();
+
+ final Consumer<Integer> doubleClick =
+ (Integer y) -> {
+ mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ // Initial state
+ checkIsCentered.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsTop.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ 1990);
+ checkLetterboxPositions.run();
+ checkIsCentered.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ 1990);
+ checkLetterboxPositions.run();
+ checkIsBottom.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsCentered.run();
+ }
+
+ @Test
public void testApplyStrategyToTranslucentActivities() {
mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
setUpDisplaySizeWithApp(2000, 1000);
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
index 9015563..2411498 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerService.java
@@ -63,6 +63,12 @@
public void createSession(String engine,
ITextToSpeechSessionCallback sessionCallback) {
synchronized (mLock) {
+ if (engine == null) {
+ runSessionCallbackMethod(
+ () -> sessionCallback.onError("Engine cannot be null"));
+ return;
+ }
+
TextToSpeechManagerPerUserService perUserService = getServiceForUserLocked(
UserHandle.getCallingUserId());
if (perUserService != null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 66711df..3f5c76d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10642,12 +10642,20 @@
* no reason to power it off. When any of the voters want to power it off, it will be turned
* off. In case of emergency, the radio will be turned on even if there are some reasons for
* powering it off, and these radio off votes will be cleared.
- * Multiple apps can vote for the same reason and the last vote will take effect. Each app is
- * responsible for its vote. A powering-off vote of a reason will be maintained until it is
- * cleared by calling {@link clearRadioPowerOffForReason} for that reason, or an emergency call
- * is made, or the device is rebooted. When an app comes backup from a crash, it needs to make
- * sure if its vote is as expected. An app can use the API {@link getRadioPowerOffReasons} to
- * check its vote.
+ * <p>
+ * Each API call is for one reason. However, an app can call the API multiple times for multiple
+ * reasons. Multiple apps can vote for the same reason but the vote of one app does not affect
+ * the vote of another app.
+ * <p>
+ * Each app is responsible for its vote. A powering-off vote for a reason of an app will be
+ * maintained until it is cleared by calling {@link #clearRadioPowerOffForReason(int)} for that
+ * reason by the app, or an emergency call is made, or the device is rebooted. When an app
+ * comes backup from a crash, it needs to make sure if its vote is as expected. An app can use
+ * the API {@link #getRadioPowerOffReasons()} to check its votes. Votes won't be removed when
+ * an app crashes.
+ * <p>
+ * User setting for power state is persistent across device reboots. This applies to all users,
+ * callers must be careful to update the off reasons when the current user changes.
*
* @param reason The reason for powering off radio.
* @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
@@ -10704,10 +10712,10 @@
}
/**
- * Get reasons for powering off radio, as requested by {@link requestRadioPowerOffForReason}.
- * If the reason set is empty, the radio is on in all cases.
+ * Get reasons for powering off radio of the calling app, as requested by
+ * {@link #requestRadioPowerOffForReason(int)}.
*
- * @return Set of reasons for powering off radio.
+ * @return Set of reasons for powering off radio of the calling app.
* @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission.
* @throws IllegalStateException if the Telephony service is not currently available.
*
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
index 858cd76..70dcc12 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
@@ -20,6 +20,7 @@
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.os.SystemClock;
+import android.util.Log;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
@@ -30,6 +31,7 @@
* Injects gestures given an {@link Instrumentation} object.
*/
public class GestureHelper {
+ private static final String TAG = GestureHelper.class.getSimpleName();
// Inserted after each motion event injection.
private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
@@ -153,6 +155,9 @@
for (int j = 0; j < coords.length; j++) {
coords[j].x += (endPoints[j].x - startPoints[j].x) / steps;
coords[j].y += (endPoints[j].y - startPoints[j].y) / steps;
+
+ // TODO: remove logging once b/269505548 is resolved
+ Log.d(TAG, "(" + coords[j].x + ", " + coords[j].y + ")");
}
eventTime = SystemClock.uptimeMillis();
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
index e36f398..f49d9c9 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
@@ -219,10 +219,18 @@
@SmallTest
public void testRecognitionEventParcelUnparcel_noData() throws Exception {
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1,
- true, 2, 3, 4, false, null, null);
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS,
+ 1 /* soundModelHandle */,
+ true /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ null /* data */,
+ 12345678 /* halEventReceivedMillis */);
- // Write to a parcel
+ // Write to a parcel
Parcel parcel = Parcel.obtain();
re.writeToParcel(parcel, 0);
@@ -236,10 +244,18 @@
@SmallTest
public void testRecognitionEventParcelUnparcel_zeroData() throws Exception {
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE, 1,
- true, 2, 3, 4, false, null, new byte[1]);
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE,
+ 1 /* soundModelHandle */,
+ true /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ new byte[1] /* data */,
+ 12345678 /* halEventReceivedMillis */);
- // Write to a parcel
+ // Write to a parcel
Parcel parcel = Parcel.obtain();
re.writeToParcel(parcel, 0);
@@ -255,10 +271,18 @@
public void testRecognitionEventParcelUnparcel_largeData() throws Exception {
byte[] data = new byte[200 * 1024];
mRandom.nextBytes(data);
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
- false, 2, 3, 4, false, null, data);
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT,
+ 1 /* soundModelHandle */,
+ false /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ data,
+ 12345678 /* halEventReceivedMillis */);
- // Write to a parcel
+ // Write to a parcel
Parcel parcel = Parcel.obtain();
re.writeToParcel(parcel, 0);
@@ -274,14 +298,20 @@
public void testRecognitionEventParcelUnparcel_largeAudioData() throws Exception {
byte[] data = new byte[200 * 1024];
mRandom.nextBytes(data);
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
- false, 2, 3, 4, true,
- (new AudioFormat.Builder())
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setSampleRate(16000)
- .build(),
- data);
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT,
+ 1 /* soundModelHandle */,
+ false /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ true /* triggerInData */,
+ new AudioFormat.Builder()
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(16000)
+ .build(),
+ data,
+ 12345678 /* halEventReceivedMillis */);
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -298,7 +328,17 @@
@SmallTest
public void testKeyphraseRecognitionEventParcelUnparcel_noKeyphrases() throws Exception {
KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, false, null, null, null);
+ SoundTrigger.RECOGNITION_STATUS_SUCCESS,
+ 1 /* soundModelHandle */,
+ true /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ null /* data */,
+ null /* keyphraseExtras */,
+ 12345678 /* halEventReceivedMillis */);
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -317,8 +357,17 @@
public void testKeyphraseRecognitionEventParcelUnparcel_zeroData() throws Exception {
KeyphraseRecognitionExtra[] kpExtra = new KeyphraseRecognitionExtra[0];
KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, false, null, new byte[1],
- kpExtra);
+ SoundTrigger.RECOGNITION_STATUS_FAILURE,
+ 2 /* soundModelHandle */,
+ true /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ new byte[1] /* data */,
+ kpExtra,
+ 12345678 /* halEventReceivedMillis */);
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -353,8 +402,17 @@
new ConfidenceLevel[0]);
KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, false, null, data,
- kpExtra);
+ SoundTrigger.RECOGNITION_STATUS_FAILURE,
+ 1 /* soundModelHandle */,
+ true /* captureAvailable */,
+ 2 /* captureSession */,
+ 3 /* captureDelayMs */,
+ 4 /* capturePreambleMs */,
+ false /* triggerInData */,
+ null /* captureFormat */,
+ data,
+ kpExtra,
+ 12345678 /* halEventReceivedMillis */);
// Write to a parcel
Parcel parcel = Parcel.obtain();
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
index e722ba5..1de965e 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -76,4 +76,4 @@
}
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
}
-}
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
index be3ed71..4c5224a 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -87,4 +87,4 @@
checkPixels(svBounds, Color.BLUE)
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
index cf4cb8c..a38019d 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -116,4 +116,4 @@
private const val TRACE_FLAGS =
(1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
}
-}
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
index bba9678..1770e32 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -100,4 +100,4 @@
INVERSE_DISPLAY(0x08)
}
}
-}
+}
\ No newline at end of file
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index a7c5479..a766bd4 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -26,8 +26,8 @@
namespace aapt {
static ApiVersion sDevelopmentSdkLevel = 10000;
-static const auto sDevelopmentSdkCodeNames =
- std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake"});
+static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>(
+ {"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream"});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 28fcc1a..e629baf 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -441,8 +441,8 @@
R"(<resources>
<public type="attr" name="finalized_res" id="0x01010001"/>
- <!-- S staged attributes (support staged resources in the same type id) -->
- <staging-public-group type="attr" first-id="0x01010050">
+ <!-- S staged attributes (Not support staged resources in the same type id) -->
+ <staging-public-group type="attr" first-id="0x01fc0050">
<public name="staged_s_res" />
</staging-public-group>
@@ -480,8 +480,8 @@
<public type="attr" name="staged_s2_res" id="0x01010003"/>
<public type="string" name="staged_s_string" id="0x01020000"/>
- <!-- S staged attributes (support staged resources in the same type id) -->
- <staging-public-group-final type="attr" first-id="0x01010050">
+ <!-- S staged attributes (Not support staged resources in the same type id) -->
+ <staging-public-group-final type="attr" first-id="0x01fc0050">
<public name="staged_s_res" />
</staging-public-group-final>
@@ -551,7 +551,7 @@
EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
EXPECT_THAT(
android_r_contents,
- HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
+ HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01fc0050; }"));
EXPECT_THAT(
android_r_contents,
HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
@@ -583,7 +583,7 @@
result = am.GetResourceId("android:attr/staged_s_res");
ASSERT_TRUE(result.has_value());
- EXPECT_THAT(*result, Eq(0x01010050));
+ EXPECT_THAT(*result, Eq(0x01fc0050));
result = am.GetResourceId("android:string/staged_s_string");
ASSERT_TRUE(result.has_value());
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index b3f98a9..5421abde 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -37,6 +37,7 @@
template <typename Id, typename Key>
struct NextIdFinder {
+ std::map<Id, Key> pre_assigned_ids_;
explicit NextIdFinder(Id start_id = 0u) : next_id_(start_id){};
// Attempts to reserve an identifier for the specified key.
@@ -55,7 +56,6 @@
Id next_id_;
bool next_id_called_ = false;
bool exhausted_ = false;
- std::map<Id, Key> pre_assigned_ids_;
typename std::map<Id, Key>::iterator next_preassigned_id_;
};
@@ -158,7 +158,7 @@
}
if (assigned_id_map_) {
- // Reserve all the IDs mentioned in the stable ID map. That way we won't assig IDs that were
+ // Reserve all the IDs mentioned in the stable ID map. That way we won't assign IDs that were
// listed in the map if they don't exist in the table.
for (const auto& stable_id_entry : *assigned_id_map_) {
const ResourceName& pre_assigned_name = stable_id_entry.first;
@@ -191,6 +191,11 @@
}
namespace {
+static const std::string_view staged_type_overlap_error =
+ "Staged public resource type IDs have conflict with non staged public resources type "
+ "IDs, please restart staged resource type ID assignment at 0xff in public-staging.xml "
+ "and also delete all the overlapping groups in public-final.xml";
+
template <typename Id, typename Key>
Result<Id> NextIdFinder<Id, Key>::ReserveId(Key key, Id id) {
CHECK(!next_id_called_) << "ReserveId cannot be called after NextId";
@@ -282,8 +287,20 @@
// another type.
auto assign_result = type_id_finder_.ReserveId(key, id.type_id());
if (!assign_result.has_value()) {
- diag->Error(android::DiagMessage() << "can't assign ID " << id << " to resource " << name
- << " because type " << assign_result.error());
+ auto pre_assigned_type = type_id_finder_.pre_assigned_ids_[id.type_id()].type;
+ bool pre_assigned_type_staged =
+ non_staged_type_ids_.find(pre_assigned_type) == non_staged_type_ids_.end();
+ auto hex_type_id = fmt::format("{:#04x}", (int)id.type_id());
+ bool current_type_staged = visibility.staged_api;
+ diag->Error(android::DiagMessage()
+ << "can't assign type ID " << hex_type_id << " to "
+ << (current_type_staged ? "staged type " : "non staged type ") << name.type.type
+ << " because this type ID have been assigned to "
+ << (pre_assigned_type_staged ? "staged type " : "non staged type ")
+ << pre_assigned_type);
+ if (pre_assigned_type_staged || current_type_staged) {
+ diag->Error(android::DiagMessage() << staged_type_overlap_error);
+ }
return false;
}
type = types_.emplace(key, TypeGroup(package_id_, id.type_id())).first;
@@ -298,6 +315,20 @@
<< " because type already has ID " << std::hex << (int)id.type_id());
return false;
}
+ } else {
+ // Ensure that staged public resources cannot have the same type name and type id with
+ // non staged public resources.
+ auto non_staged_type = non_staged_type_ids_.find(name.type.type);
+ if (non_staged_type != non_staged_type_ids_.end() && non_staged_type->second == id.type_id()) {
+ diag->Error(
+ android::DiagMessage()
+ << "can`t assign type ID " << fmt::format("{:#04x}", (int)id.type_id())
+ << " to staged type " << name.type.type << " because type ID "
+ << fmt::format("{:#04x}", (int)id.type_id())
+ << " already has been assigned to a non staged resource type with the same type name");
+ diag->Error(android::DiagMessage() << staged_type_overlap_error);
+ return false;
+ }
}
auto assign_result = type->second.ReserveId(name, id);
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 8911dad..ce45b7c 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -117,14 +117,28 @@
}
TEST_F(IdAssignerTests, FailWhenTypeHasTwoNonStagedIdsRegardlessOfStagedId) {
- auto table = test::ResourceTableBuilder()
- .AddSimple("android:attr/foo", ResourceId(0x01050000))
- .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
- .Add(NewResourceBuilder("android:attr/staged_baz")
- .SetId(0x01ff0000)
- .SetVisibility({.staged_api = true})
- .Build())
- .Build();
+ auto table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01050000))
+ .AddSimple("android:attr/bar", ResourceId(0x01ff0006))
+ .Add(NewResourceBuilder("android:attr/staged_baz")
+ .SetId(0x01ff0000)
+ .SetVisibility({.staged_api = true, .level = Visibility::Level::kPublic})
+ .Build())
+ .Build();
+ IdAssigner assigner;
+ ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
+}
+
+TEST_F(IdAssignerTests, FailWhenTypeHaveBothStagedAndNonStagedIds) {
+ auto table =
+ test::ResourceTableBuilder()
+ .AddSimple("android:attr/foo", ResourceId(0x01010000))
+ .Add(NewResourceBuilder("android:bool/staged_baz")
+ .SetId(0x01010001)
+ .SetVisibility({.staged_api = true, .level = Visibility::Level::kPublic})
+ .Build())
+ .Build();
IdAssigner assigner;
ASSERT_FALSE(assigner.Consume(context.get(), table.get()));
}
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt
index c3e0428..28027a7 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/RegisterReceiverFlagDetector.kt
@@ -111,7 +111,7 @@
if (!isProtected) {
val actionsList = unprotectedActionsList.joinToString(", ", "", "", -1, "")
- val message = "$receiverArg is missing 'RECEIVED_EXPORTED` or 'RECEIVE_NOT_EXPORTED' " +
+ val message = "$receiverArg is missing 'RECEIVER_EXPORTED` or 'RECEIVER_NOT_EXPORTED' " +
"flag for unprotected broadcast(s) registered for $actionsList."
if (flagsArg == null) {
context.report(
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index f7560a7..75b0073 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -321,6 +321,34 @@
)
}
+ fun testDoesDetectIssuesShortStringsNotAllowed() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass121 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteral() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass121.java:6: Error: The method \
+ TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
+ which differs from the overridden method Stub.testMethodAnyLiteral: \
+ @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
+ The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteral() {}
+ ~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """.addLineContinuation()
+ )
+ }
+
/* Stubs */
// A service with permission annotation on the method.
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl
deleted file mode 100644
index 35d5c15..0000000
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2023 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.wifi.sharedconnectivity.app;
-
-parcelable DeviceInfo;
\ No newline at end of file
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.aidl
similarity index 95%
rename from wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
rename to wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.aidl
index 6cc4cfe..f9c4829 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.aidl
@@ -16,4 +16,4 @@
package android.net.wifi.sharedconnectivity.app;
-parcelable TetherNetwork;
+parcelable HotspotNetwork;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
similarity index 77%
rename from wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
rename to wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
index 7b591d3..5cf19fb 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
@@ -33,14 +33,14 @@
import java.util.Set;
/**
- * A data class representing an Instant Tether network.
+ * A data class representing a hotspot network.
* This class is used in IPC calls between the implementer of {@link SharedConnectivityService} and
* the consumers of {@link com.android.wifitrackerlib}.
*
* @hide
*/
@SystemApi
-public final class TetherNetwork implements Parcelable {
+public final class HotspotNetwork implements Parcelable {
/**
* Remote device is connected to the internet via an unknown connection.
*/
@@ -71,33 +71,44 @@
NETWORK_TYPE_WIFI,
NETWORK_TYPE_ETHERNET
})
- public @interface NetworkType {}
+ public @interface NetworkType {
+ }
private final long mDeviceId;
- private final DeviceInfo mDeviceInfo;
- @NetworkType private final int mNetworkType;
+ private final NetworkProviderInfo mNetworkProviderInfo;
+ @NetworkType
+ private final int mNetworkType;
private final String mNetworkName;
- @Nullable private final String mHotspotSsid;
- @Nullable private final String mHotspotBssid;
- @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes;
+ @Nullable
+ private final String mHotspotSsid;
+ @Nullable
+ private final String mHotspotBssid;
+ @Nullable
+ @SecurityType
+ private final ArraySet<Integer> mHotspotSecurityTypes;
/**
- * Builder class for {@link TetherNetwork}.
+ * Builder class for {@link HotspotNetwork}.
*/
public static final class Builder {
private long mDeviceId = -1;
- private DeviceInfo mDeviceInfo;
- @NetworkType private int mNetworkType;
+ private NetworkProviderInfo mNetworkProviderInfo;
+ @NetworkType
+ private int mNetworkType;
private String mNetworkName;
- @Nullable private String mHotspotSsid;
- @Nullable private String mHotspotBssid;
- @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes =
+ @Nullable
+ private String mHotspotSsid;
+ @Nullable
+ private String mHotspotBssid;
+ @Nullable
+ @SecurityType
+ private final ArraySet<Integer> mHotspotSecurityTypes =
new ArraySet<>();
/**
* Set the remote device ID.
*
- * @param deviceId Locally unique ID for this Instant Tether network.
+ * @param deviceId Locally unique ID for this Hotspot network.
* @return Returns the Builder object.
*/
@NonNull
@@ -109,12 +120,12 @@
/**
* Sets information about the device providing connectivity.
*
- * @param deviceInfo The device information object.
+ * @param networkProviderInfo The device information object.
* @return Returns the Builder object.
*/
@NonNull
- public Builder setDeviceInfo(@NonNull DeviceInfo deviceInfo) {
- mDeviceInfo = deviceInfo;
+ public Builder setNetworkProviderInfo(@NonNull NetworkProviderInfo networkProviderInfo) {
+ mNetworkProviderInfo = networkProviderInfo;
return this;
}
@@ -125,7 +136,7 @@
* @return Returns the Builder object.
*/
@NonNull
- public Builder setNetworkType(@NetworkType int networkType) {
+ public Builder setHostNetworkType(@NetworkType int networkType) {
mNetworkType = networkType;
return this;
}
@@ -179,15 +190,15 @@
}
/**
- * Builds the {@link TetherNetwork} object.
+ * Builds the {@link HotspotNetwork} object.
*
- * @return Returns the built {@link TetherNetwork} object.
+ * @return Returns the built {@link HotspotNetwork} object.
*/
@NonNull
- public TetherNetwork build() {
- return new TetherNetwork(
+ public HotspotNetwork build() {
+ return new HotspotNetwork(
mDeviceId,
- mDeviceInfo,
+ mNetworkProviderInfo,
mNetworkType,
mNetworkName,
mHotspotSsid,
@@ -209,9 +220,9 @@
}
}
- private TetherNetwork(
+ private HotspotNetwork(
long deviceId,
- DeviceInfo deviceInfo,
+ NetworkProviderInfo networkProviderInfo,
@NetworkType int networkType,
@NonNull String networkName,
@Nullable String hotspotSsid,
@@ -221,7 +232,7 @@
networkType,
networkName);
mDeviceId = deviceId;
- mDeviceInfo = deviceInfo;
+ mNetworkProviderInfo = networkProviderInfo;
mNetworkType = networkType;
mNetworkName = networkName;
mHotspotSsid = hotspotSsid;
@@ -232,7 +243,7 @@
/**
* Gets the remote device ID.
*
- * @return Returns the locally unique ID for this Instant Tether network.
+ * @return Returns the locally unique ID for this Hotspot network.
*/
public long getDeviceId() {
return mDeviceId;
@@ -241,11 +252,11 @@
/**
* Gets information about the device providing connectivity.
*
- * @return Returns the information of the device providing the Instant Tether network.
+ * @return Returns the information of the device providing the Hotspot network.
*/
@NonNull
- public DeviceInfo getDeviceInfo() {
- return mDeviceInfo;
+ public NetworkProviderInfo getNetworkProviderInfo() {
+ return mNetworkProviderInfo;
}
/**
@@ -254,7 +265,7 @@
* @return Returns the network type as represented by IntDef {@link NetworkType}.
*/
@NetworkType
- public int getNetworkType() {
+ public int getHostNetworkType() {
return mNetworkType;
}
@@ -301,11 +312,11 @@
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof TetherNetwork)) return false;
- TetherNetwork other = (TetherNetwork) obj;
+ if (!(obj instanceof HotspotNetwork)) return false;
+ HotspotNetwork other = (HotspotNetwork) obj;
return mDeviceId == other.getDeviceId()
- && Objects.equals(mDeviceInfo, other.getDeviceInfo())
- && mNetworkType == other.getNetworkType()
+ && Objects.equals(mNetworkProviderInfo, other.getNetworkProviderInfo())
+ && mNetworkType == other.getHostNetworkType()
&& Objects.equals(mNetworkName, other.getNetworkName())
&& Objects.equals(mHotspotSsid, other.getHotspotSsid())
&& Objects.equals(mHotspotBssid, other.getHotspotBssid())
@@ -314,8 +325,8 @@
@Override
public int hashCode() {
- return Objects.hash(mDeviceId, mDeviceInfo, mNetworkName, mHotspotSsid, mHotspotBssid,
- mHotspotSecurityTypes);
+ return Objects.hash(mDeviceId, mNetworkProviderInfo, mNetworkName, mHotspotSsid,
+ mHotspotBssid, mHotspotSecurityTypes);
}
@Override
@@ -326,7 +337,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mDeviceId);
- mDeviceInfo.writeToParcel(dest, flags);
+ mNetworkProviderInfo.writeToParcel(dest, flags);
dest.writeInt(mNetworkType);
dest.writeString(mNetworkName);
dest.writeString(mHotspotSsid);
@@ -335,36 +346,36 @@
}
/**
- * Creates a {@link TetherNetwork} object from a parcel.
+ * Creates a {@link HotspotNetwork} object from a parcel.
*
* @hide
*/
@NonNull
- public static TetherNetwork readFromParcel(@NonNull Parcel in) {
- return new TetherNetwork(in.readLong(), DeviceInfo.readFromParcel(in),
+ public static HotspotNetwork readFromParcel(@NonNull Parcel in) {
+ return new HotspotNetwork(in.readLong(), NetworkProviderInfo.readFromParcel(in),
in.readInt(), in.readString(), in.readString(), in.readString(),
(ArraySet<Integer>) in.readArraySet(null));
}
@NonNull
- public static final Creator<TetherNetwork> CREATOR = new Creator<>() {
+ public static final Creator<HotspotNetwork> CREATOR = new Creator<>() {
@Override
- public TetherNetwork createFromParcel(Parcel in) {
+ public HotspotNetwork createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
- public TetherNetwork[] newArray(int size) {
- return new TetherNetwork[size];
+ public HotspotNetwork[] newArray(int size) {
+ return new HotspotNetwork[size];
}
};
@Override
public String toString() {
- return new StringBuilder("TetherNetwork[")
+ return new StringBuilder("HotspotNetwork[")
.append("deviceId=").append(mDeviceId)
.append(", networkType=").append(mNetworkType)
- .append(", deviceInfo=").append(mDeviceInfo.toString())
+ .append(", networkProviderInfo=").append(mNetworkProviderInfo.toString())
.append(", networkName=").append(mNetworkName)
.append(", hotspotSsid=").append(mHotspotSsid)
.append(", hotspotBssid=").append(mHotspotBssid)
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.aidl
similarity index 93%
copy from wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
copy to wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.aidl
index 6cc4cfe..d32d15e 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.aidl
@@ -16,4 +16,4 @@
package android.net.wifi.sharedconnectivity.app;
-parcelable TetherNetwork;
+parcelable HotspotNetworkConnectionStatus;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
similarity index 65%
rename from wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.java
rename to wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
index 3cf44ed..7a50223 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatus.java
@@ -28,23 +28,23 @@
import java.util.Objects;
/**
- * The status of a connection to an instant tether network after the client called
- * {@link SharedConnectivityManager#connectTetherNetwork}.
+ * The status of a connection to a hotspot network after the client called
+ * {@link SharedConnectivityManager#connectHotspotNetwork}.
*
* @hide
*/
@SystemApi
-public final class TetherNetworkConnectionStatus implements Parcelable {
+public final class HotspotNetworkConnectionStatus implements Parcelable {
/**
* Connection status is unknown.
*/
- public static final int CONNECTION_STATUS_UNKNOWN = 0;
+ public static final int CONNECTION_STATUS_UNKNOWN = 0;
/**
* The connection is being initiated.
*/
- public static final int CONNECTION_STATUS_ENABLING_HOTSPOT = 1;
+ public static final int CONNECTION_STATUS_ENABLING_HOTSPOT = 1;
/**
* Device providing the hotspot failed to initiate it.
@@ -102,22 +102,23 @@
CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT,
CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED,
})
- public @interface ConnectionStatus {}
+ public @interface ConnectionStatus {
+ }
- @ConnectionStatus private final int mStatus;
- private final TetherNetwork mTetherNetwork;
+ @ConnectionStatus
+ private final int mStatus;
+ private final HotspotNetwork mHotspotNetwork;
private final Bundle mExtras;
/**
- * Builder class for {@link TetherNetworkConnectionStatus}.
+ * Builder class for {@link HotspotNetworkConnectionStatus}.
*/
public static final class Builder {
- @ConnectionStatus private int mStatus;
- private TetherNetwork mTetherNetwork;
+ @ConnectionStatus
+ private int mStatus;
+ private HotspotNetwork mHotspotNetwork;
private Bundle mExtras;
- public Builder() {}
-
/**
* Sets the status of the connection
*
@@ -130,13 +131,13 @@
}
/**
- * Sets the {@link TetherNetwork} object of the connection.
+ * Sets the {@link HotspotNetwork} object of the connection.
*
* @return Returns the Builder object.
*/
@NonNull
- public Builder setTetherNetwork(@NonNull TetherNetwork tetherNetwork) {
- mTetherNetwork = tetherNetwork;
+ public Builder setHotspotNetwork(@NonNull HotspotNetwork hotspotNetwork) {
+ mHotspotNetwork = hotspotNetwork;
return this;
}
@@ -152,20 +153,21 @@
}
/**
- * Builds the {@link TetherNetworkConnectionStatus} object.
+ * Builds the {@link HotspotNetworkConnectionStatus} object.
*
- * @return Returns the built {@link TetherNetworkConnectionStatus} object.
+ * @return Returns the built {@link HotspotNetworkConnectionStatus} object.
*/
@NonNull
- public TetherNetworkConnectionStatus build() {
- return new TetherNetworkConnectionStatus(mStatus, mTetherNetwork, mExtras);
+ public HotspotNetworkConnectionStatus build() {
+ return new HotspotNetworkConnectionStatus(mStatus, mHotspotNetwork, mExtras);
}
}
- private TetherNetworkConnectionStatus(@ConnectionStatus int status, TetherNetwork tetherNetwork,
+ private HotspotNetworkConnectionStatus(@ConnectionStatus int status,
+ HotspotNetwork hotspotNetwork,
Bundle extras) {
mStatus = status;
- mTetherNetwork = tetherNetwork;
+ mHotspotNetwork = hotspotNetwork;
mExtras = extras;
}
@@ -180,13 +182,13 @@
}
/**
- * Gets the {@link TetherNetwork} object of the connection.
+ * Gets the {@link HotspotNetwork} object of the connection.
*
- * @return Returns a TetherNetwork object.
+ * @return Returns a HotspotNetwork object.
*/
@NonNull
- public TetherNetwork getTetherNetwork() {
- return mTetherNetwork;
+ public HotspotNetwork getHotspotNetwork() {
+ return mHotspotNetwork;
}
/**
@@ -201,15 +203,15 @@
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof TetherNetworkConnectionStatus)) return false;
- TetherNetworkConnectionStatus other = (TetherNetworkConnectionStatus) obj;
+ if (!(obj instanceof HotspotNetworkConnectionStatus)) return false;
+ HotspotNetworkConnectionStatus other = (HotspotNetworkConnectionStatus) obj;
return mStatus == other.getStatus()
- && Objects.equals(mTetherNetwork, other.getTetherNetwork());
+ && Objects.equals(mHotspotNetwork, other.getHotspotNetwork());
}
@Override
public int hashCode() {
- return Objects.hash(mStatus, mTetherNetwork);
+ return Objects.hash(mStatus, mHotspotNetwork);
}
@Override
@@ -220,39 +222,39 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mStatus);
- mTetherNetwork.writeToParcel(dest, flags);
+ mHotspotNetwork.writeToParcel(dest, flags);
dest.writeBundle(mExtras);
}
/**
- * Creates a {@link TetherNetworkConnectionStatus} object from a parcel.
+ * Creates a {@link HotspotNetworkConnectionStatus} object from a parcel.
*
* @hide
*/
@NonNull
- public static TetherNetworkConnectionStatus readFromParcel(@NonNull Parcel in) {
- return new TetherNetworkConnectionStatus(in.readInt(),
- TetherNetwork.readFromParcel(in), in.readBundle());
+ public static HotspotNetworkConnectionStatus readFromParcel(@NonNull Parcel in) {
+ return new HotspotNetworkConnectionStatus(in.readInt(),
+ HotspotNetwork.readFromParcel(in), in.readBundle());
}
@NonNull
- public static final Creator<TetherNetworkConnectionStatus> CREATOR = new Creator<>() {
+ public static final Creator<HotspotNetworkConnectionStatus> CREATOR = new Creator<>() {
@Override
- public TetherNetworkConnectionStatus createFromParcel(Parcel in) {
+ public HotspotNetworkConnectionStatus createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
- public TetherNetworkConnectionStatus[] newArray(int size) {
- return new TetherNetworkConnectionStatus[size];
+ public HotspotNetworkConnectionStatus[] newArray(int size) {
+ return new HotspotNetworkConnectionStatus[size];
}
};
@Override
public String toString() {
- return new StringBuilder("TetherNetworkConnectionStatus[")
+ return new StringBuilder("HotspotNetworkConnectionStatus[")
.append("status=").append(mStatus)
- .append("tether network=").append(mTetherNetwork.toString())
+ .append("hotspot network=").append(mHotspotNetwork.toString())
.append("extras=").append(mExtras.toString())
.append("]").toString();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
index 1fb1c63..d33e08f 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,21 +39,28 @@
*/
@SystemApi
public final class KnownNetwork implements Parcelable {
+
+ /**
+ * Network source is unknown.
+ */
+ public static final int NETWORK_SOURCE_UNKNOWN = 0;
+
/**
* Network is known by a nearby device with the same user account.
*/
- public static final int NETWORK_SOURCE_NEARBY_SELF = 0;
+ public static final int NETWORK_SOURCE_NEARBY_SELF = 1;
/**
* Network is known via cloud storage associated with this device's user account.
*/
- public static final int NETWORK_SOURCE_CLOUD_SELF = 1;
+ public static final int NETWORK_SOURCE_CLOUD_SELF = 2;
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ NETWORK_SOURCE_UNKNOWN,
NETWORK_SOURCE_NEARBY_SELF,
NETWORK_SOURCE_CLOUD_SELF
})
@@ -61,7 +69,7 @@
@NetworkSource private final int mNetworkSource;
private final String mSsid;
@SecurityType private final ArraySet<Integer> mSecurityTypes;
- private final DeviceInfo mDeviceInfo;
+ private final NetworkProviderInfo mNetworkProviderInfo;
/**
* Builder class for {@link KnownNetwork}.
@@ -70,7 +78,7 @@
@NetworkSource private int mNetworkSource = -1;
private String mSsid;
@SecurityType private final ArraySet<Integer> mSecurityTypes = new ArraySet<>();
- private android.net.wifi.sharedconnectivity.app.DeviceInfo mDeviceInfo;
+ private NetworkProviderInfo mNetworkProviderInfo;
/**
* Sets the indicated source of the known network.
@@ -110,13 +118,14 @@
/**
* Sets the device information of the device providing connectivity.
+ * Must be set if network source is {@link KnownNetwork#NETWORK_SOURCE_NEARBY_SELF}.
*
- * @param deviceInfo The device information object.
+ * @param networkProviderInfo The device information object.
* @return Returns the Builder object.
*/
@NonNull
- public Builder setDeviceInfo(@NonNull DeviceInfo deviceInfo) {
- mDeviceInfo = deviceInfo;
+ public Builder setNetworkProviderInfo(@Nullable NetworkProviderInfo networkProviderInfo) {
+ mNetworkProviderInfo = networkProviderInfo;
return this;
}
@@ -131,13 +140,15 @@
mNetworkSource,
mSsid,
mSecurityTypes,
- mDeviceInfo);
+ mNetworkProviderInfo);
}
}
- private static void validate(int networkSource, String ssid, Set<Integer> securityTypes) {
- if (networkSource != NETWORK_SOURCE_CLOUD_SELF && networkSource
- != NETWORK_SOURCE_NEARBY_SELF) {
+ private static void validate(int networkSource, String ssid, Set<Integer> securityTypes,
+ NetworkProviderInfo networkProviderInfo) {
+ if (networkSource != NETWORK_SOURCE_UNKNOWN
+ && networkSource != NETWORK_SOURCE_CLOUD_SELF
+ && networkSource != NETWORK_SOURCE_NEARBY_SELF) {
throw new IllegalArgumentException("Illegal network source");
}
if (TextUtils.isEmpty(ssid)) {
@@ -146,18 +157,22 @@
if (securityTypes.isEmpty()) {
throw new IllegalArgumentException("SecurityTypes must be set");
}
+ if (networkSource == NETWORK_SOURCE_NEARBY_SELF && networkProviderInfo == null) {
+ throw new IllegalArgumentException("Device info must be provided when network source"
+ + " is NETWORK_SOURCE_NEARBY_SELF");
+ }
}
private KnownNetwork(
@NetworkSource int networkSource,
@NonNull String ssid,
@NonNull @SecurityType ArraySet<Integer> securityTypes,
- @NonNull DeviceInfo deviceInfo) {
- validate(networkSource, ssid, securityTypes);
+ @Nullable NetworkProviderInfo networkProviderInfo) {
+ validate(networkSource, ssid, securityTypes, networkProviderInfo);
mNetworkSource = networkSource;
mSsid = ssid;
mSecurityTypes = new ArraySet<>(securityTypes);
- mDeviceInfo = deviceInfo;
+ mNetworkProviderInfo = networkProviderInfo;
}
/**
@@ -194,11 +209,12 @@
/**
* Gets the device information of the device providing connectivity.
*
- * @return Returns the information of the device providing the known network.
+ * @return Returns the information of the device providing the known network. Can be null if the
+ * network source is cloud or unknown.
*/
- @NonNull
- public DeviceInfo getDeviceInfo() {
- return mDeviceInfo;
+ @Nullable
+ public NetworkProviderInfo getNetworkProviderInfo() {
+ return mNetworkProviderInfo;
}
@Override
@@ -208,12 +224,12 @@
return mNetworkSource == other.getNetworkSource()
&& Objects.equals(mSsid, other.getSsid())
&& Objects.equals(mSecurityTypes, other.getSecurityTypes())
- && Objects.equals(mDeviceInfo, other.getDeviceInfo());
+ && Objects.equals(mNetworkProviderInfo, other.getNetworkProviderInfo());
}
@Override
public int hashCode() {
- return Objects.hash(mNetworkSource, mSsid, mSecurityTypes, mDeviceInfo);
+ return Objects.hash(mNetworkSource, mSsid, mSecurityTypes, mNetworkProviderInfo);
}
@Override
@@ -226,7 +242,7 @@
dest.writeInt(mNetworkSource);
dest.writeString(mSsid);
dest.writeArraySet(mSecurityTypes);
- mDeviceInfo.writeToParcel(dest, flags);
+ mNetworkProviderInfo.writeToParcel(dest, flags);
}
/**
@@ -238,7 +254,7 @@
public static KnownNetwork readFromParcel(@NonNull Parcel in) {
return new KnownNetwork(in.readInt(), in.readString(),
(ArraySet<Integer>) in.readArraySet(null),
- DeviceInfo.readFromParcel(in));
+ NetworkProviderInfo.readFromParcel(in));
}
@NonNull
@@ -260,7 +276,7 @@
.append("NetworkSource=").append(mNetworkSource)
.append(", ssid=").append(mSsid)
.append(", securityTypes=").append(mSecurityTypes.toString())
- .append(", deviceInfo=").append(mDeviceInfo.toString())
+ .append(", networkProviderInfo=").append(mNetworkProviderInfo.toString())
.append("]").toString();
}
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.aidl
similarity index 95%
copy from wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
copy to wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.aidl
index 6cc4cfe..f3cbbc2 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.aidl
@@ -16,4 +16,4 @@
package android.net.wifi.sharedconnectivity.app;
-parcelable TetherNetwork;
+parcelable NetworkProviderInfo;
\ No newline at end of file
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
similarity index 84%
rename from wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java
rename to wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index 52abf33..a436571c 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/DeviceInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -36,7 +36,7 @@
* @hide
*/
@SystemApi
-public final class DeviceInfo implements Parcelable {
+public final class NetworkProviderInfo implements Parcelable {
/**
* Device type providing connectivity is unknown.
@@ -59,6 +59,16 @@
public static final int DEVICE_TYPE_LAPTOP = 3;
/**
+ * Device providing connectivity is a watch.
+ */
+ public static final int DEVICE_TYPE_WATCH = 4;
+
+ /**
+ * Device providing connectivity is a watch.
+ */
+ public static final int DEVICE_TYPE_AUTO = 5;
+
+ /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -66,7 +76,9 @@
DEVICE_TYPE_UNKNOWN,
DEVICE_TYPE_PHONE,
DEVICE_TYPE_TABLET,
- DEVICE_TYPE_LAPTOP
+ DEVICE_TYPE_LAPTOP,
+ DEVICE_TYPE_WATCH,
+ DEVICE_TYPE_AUTO
})
public @interface DeviceType {}
@@ -77,7 +89,7 @@
private final int mConnectionStrength;
/**
- * Builder class for {@link DeviceInfo}.
+ * Builder class for {@link NetworkProviderInfo}.
*/
public static final class Builder {
private int mDeviceType;
@@ -149,13 +161,13 @@
}
/**
- * Builds the {@link DeviceInfo} object.
+ * Builds the {@link NetworkProviderInfo} object.
*
- * @return Returns the built {@link DeviceInfo} object.
+ * @return Returns the built {@link NetworkProviderInfo} object.
*/
@NonNull
- public DeviceInfo build() {
- return new DeviceInfo(mDeviceType, mDeviceName, mModelName, mBatteryPercentage,
+ public NetworkProviderInfo build() {
+ return new NetworkProviderInfo(mDeviceType, mDeviceName, mModelName, mBatteryPercentage,
mConnectionStrength);
}
}
@@ -163,7 +175,8 @@
private static void validate(int deviceType, String deviceName, String modelName,
int batteryPercentage, int connectionStrength) {
if (deviceType != DEVICE_TYPE_UNKNOWN && deviceType != DEVICE_TYPE_PHONE
- && deviceType != DEVICE_TYPE_TABLET && deviceType != DEVICE_TYPE_LAPTOP) {
+ && deviceType != DEVICE_TYPE_TABLET && deviceType != DEVICE_TYPE_LAPTOP
+ && deviceType != DEVICE_TYPE_WATCH && deviceType != DEVICE_TYPE_AUTO) {
throw new IllegalArgumentException("Illegal device type");
}
if (Objects.isNull(deviceName)) {
@@ -180,7 +193,7 @@
}
}
- private DeviceInfo(@DeviceType int deviceType, @NonNull String deviceName,
+ private NetworkProviderInfo(@DeviceType int deviceType, @NonNull String deviceName,
@NonNull String modelName, int batteryPercentage, int connectionStrength) {
validate(deviceType, deviceName, modelName, batteryPercentage, connectionStrength);
mDeviceType = deviceType;
@@ -242,8 +255,8 @@
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof DeviceInfo)) return false;
- DeviceInfo other = (DeviceInfo) obj;
+ if (!(obj instanceof NetworkProviderInfo)) return false;
+ NetworkProviderInfo other = (NetworkProviderInfo) obj;
return mDeviceType == other.getDeviceType()
&& Objects.equals(mDeviceName, other.mDeviceName)
&& Objects.equals(mModelName, other.mModelName)
@@ -271,32 +284,32 @@
}
/**
- * Creates a {@link DeviceInfo} object from a parcel.
+ * Creates a {@link NetworkProviderInfo} object from a parcel.
*
* @hide
*/
@NonNull
- public static DeviceInfo readFromParcel(@NonNull Parcel in) {
- return new DeviceInfo(in.readInt(), in.readString(), in.readString(), in.readInt(),
+ public static NetworkProviderInfo readFromParcel(@NonNull Parcel in) {
+ return new NetworkProviderInfo(in.readInt(), in.readString(), in.readString(), in.readInt(),
in.readInt());
}
@NonNull
- public static final Creator<DeviceInfo> CREATOR = new Creator<DeviceInfo>() {
+ public static final Creator<NetworkProviderInfo> CREATOR = new Creator<NetworkProviderInfo>() {
@Override
- public DeviceInfo createFromParcel(Parcel in) {
+ public NetworkProviderInfo createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
- public DeviceInfo[] newArray(int size) {
- return new DeviceInfo[size];
+ public NetworkProviderInfo[] newArray(int size) {
+ return new NetworkProviderInfo[size];
}
};
@Override
public String toString() {
- return new StringBuilder("DeviceInfo[")
+ return new StringBuilder("NetworkProviderInfo[")
.append("deviceType=").append(mDeviceType)
.append(", deviceName=").append(mDeviceName)
.append(", modelName=").append(mModelName)
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
index d2b9be7..eb04df6 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
@@ -32,14 +32,16 @@
public interface SharedConnectivityClientCallback {
/**
* This method is being called by {@link SharedConnectivityService} to notify of a change in the
- * list of available Tether Networks.
- * @param networks Updated Tether Network list.
+ * list of available Hotspot Networks.
+ *
+ * @param networks Updated Hotspot Network list.
*/
- void onTetherNetworksUpdated(@NonNull List<TetherNetwork> networks);
+ void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks);
/**
* This method is being called by {@link SharedConnectivityService} to notify of a change in the
* list of available Known Networks.
+ *
* @param networks Updated Known Network list.
*/
void onKnownNetworksUpdated(@NonNull List<KnownNetwork> networks);
@@ -47,20 +49,23 @@
/**
* This method is being called by {@link SharedConnectivityService} to notify of a change in the
* state of share connectivity settings.
+ *
* @param state The new state.
*/
void onSharedConnectivitySettingsChanged(@NonNull SharedConnectivitySettingsState state);
/**
* This method is being called by {@link SharedConnectivityService} to notify of a change in the
- * status of the current tether network connection.
+ * status of the current hotspot network connection.
+ *
* @param status The new status.
*/
- void onTetherNetworkConnectionStatusChanged(@NonNull TetherNetworkConnectionStatus status);
+ void onHotspotNetworkConnectionStatusChanged(@NonNull HotspotNetworkConnectionStatus status);
/**
* This method is being called by {@link SharedConnectivityService} to notify of a change in the
* status of the current known network connection.
+ *
* @param status The new status.
*/
void onKnownNetworkConnectionStatusChanged(@NonNull KnownNetworkConnectionStatus status);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index a19510b..684b385d 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -48,7 +49,7 @@
* This class is the library used by consumers of Shared Connectivity data to bind to the service,
* receive callbacks from, and send user actions to the service.
*
- * The methods {@link #connectTetherNetwork}, {@link #disconnectTetherNetwork},
+ * The methods {@link #connectHotspotNetwork}, {@link #disconnectHotspotNetwork},
* {@link #connectKnownNetwork} and {@link #forgetKnownNetwork} are not valid and will return false
* if not called between {@link SharedConnectivityClientCallback#onServiceConnected()}
* and {@link SharedConnectivityClientCallback#onServiceDisconnected()} or if
@@ -73,12 +74,11 @@
mCallback = callback;
}
- @Override
- public void onTetherNetworksUpdated(@NonNull List<TetherNetwork> networks) {
+ public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) {
if (mCallback != null) {
final long token = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mCallback.onTetherNetworksUpdated(networks));
+ mExecutor.execute(() -> mCallback.onHotspotNetworksUpdated(networks));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -110,14 +110,13 @@
}
}
- @Override
- public void onTetherNetworkConnectionStatusChanged(
- @NonNull TetherNetworkConnectionStatus status) {
+ public void onHotspotNetworkConnectionStatusChanged(
+ @NonNull HotspotNetworkConnectionStatus status) {
if (mCallback != null) {
final long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(() ->
- mCallback.onTetherNetworkConnectionStatusChanged(status));
+ mCallback.onHotspotNetworkConnectionStatusChanged(status));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -151,7 +150,7 @@
* Creates a new instance of {@link SharedConnectivityManager}.
*
* Automatically binds to implementation of {@link SharedConnectivityService} specified in
- * device overlay.
+ * the device overlay.
*
* @return An instance of {@link SharedConnectivityManager} or null if the shared connectivity
* service is not found.
@@ -258,17 +257,18 @@
}
/**
- * Registers a callback for receiving updates to the list of Tether Networks, Known Networks,
- * shared connectivity settings state, tether network connection status and known network
+ * Registers a callback for receiving updates to the list of Hotspot Networks, Known Networks,
+ * shared connectivity settings state, hotspot network connection status and known network
* connection status.
* The {@link SharedConnectivityClientCallback#onRegisterCallbackFailed} will be called if the
* registration failed.
*
* @param executor The Executor used to invoke the callback.
* @param callback The callback of type {@link SharedConnectivityClientCallback} that is invoked
- * when the service updates either the list of Tether Networks or Known
- * Networks.
+ * when the service updates its data.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
public void registerCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull SharedConnectivityClientCallback callback) {
Objects.requireNonNull(executor, "executor cannot be null");
@@ -297,6 +297,8 @@
*
* @return Returns true if the callback was successfully unregistered, false otherwise.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
public boolean unregisterCallback(
@NonNull SharedConnectivityClientCallback callback) {
Objects.requireNonNull(callback, "callback cannot be null");
@@ -325,26 +327,28 @@
return true;
}
- /**
+ /**
* Send command to the implementation of {@link SharedConnectivityService} requesting connection
- * to the specified Tether Network.
+ * to the specified Hotspot Network.
*
- * @param network {@link TetherNetwork} object representing the network the user has requested
+ * @param network {@link HotspotNetwork} object representing the network the user has requested
* a connection to.
* @return Returns true if the service received the command. Does not guarantee that the
- * connection was successful.
+ * connection was successful.
*/
- public boolean connectTetherNetwork(@NonNull TetherNetwork network) {
- Objects.requireNonNull(network, "Tether network cannot be null");
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public boolean connectHotspotNetwork(@NonNull HotspotNetwork network) {
+ Objects.requireNonNull(network, "Hotspot network cannot be null");
if (mService == null) {
return false;
}
try {
- mService.connectTetherNetwork(network);
+ mService.connectHotspotNetwork(network);
} catch (RemoteException e) {
- Log.e(TAG, "Exception in connectTetherNetwork", e);
+ Log.e(TAG, "Exception in connectHotspotNetwork", e);
return false;
}
return true;
@@ -352,22 +356,24 @@
/**
* Send command to the implementation of {@link SharedConnectivityService} requesting
- * disconnection from the active Tether Network.
+ * disconnection from the active Hotspot Network.
*
- * @param network {@link TetherNetwork} object representing the network the user has requested
+ * @param network {@link HotspotNetwork} object representing the network the user has requested
* to disconnect from.
* @return Returns true if the service received the command. Does not guarantee that the
- * disconnection was successful.
+ * disconnection was successful.
*/
- public boolean disconnectTetherNetwork(@NonNull TetherNetwork network) {
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public boolean disconnectHotspotNetwork(@NonNull HotspotNetwork network) {
if (mService == null) {
return false;
}
try {
- mService.disconnectTetherNetwork(network);
+ mService.disconnectHotspotNetwork(network);
} catch (RemoteException e) {
- Log.e(TAG, "Exception in disconnectTetherNetwork", e);
+ Log.e(TAG, "Exception in disconnectHotspotNetwork", e);
return false;
}
return true;
@@ -380,8 +386,10 @@
* @param network {@link KnownNetwork} object representing the network the user has requested
* a connection to.
* @return Returns true if the service received the command. Does not guarantee that the
- * connection was successful.
+ * connection was successful.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
public boolean connectKnownNetwork(@NonNull KnownNetwork network) {
Objects.requireNonNull(network, "Known network cannot be null");
@@ -403,8 +411,10 @@
* the specified Known Network from the list of Known Networks.
*
* @return Returns true if the service received the command. Does not guarantee that the
- * forget action was successful.
+ * forget action was successful.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
public boolean forgetKnownNetwork(@NonNull KnownNetwork network) {
Objects.requireNonNull(network, "Known network cannot be null");
@@ -422,20 +432,22 @@
}
/**
- * Gets the list of tether networks the user can select to connect to.
+ * Gets the list of hotspot networks the user can select to connect to.
*
- * @return Returns a {@link List} of {@link TetherNetwork} objects, empty list on failure.
+ * @return Returns a {@link List} of {@link HotspotNetwork} objects, empty list on failure.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@NonNull
- public List<TetherNetwork> getTetherNetworks() {
+ public List<HotspotNetwork> getHotspotNetworks() {
if (mService == null) {
return List.of();
}
try {
- return mService.getTetherNetworks();
+ return mService.getHotspotNetworks();
} catch (RemoteException e) {
- Log.e(TAG, "Exception in getTetherNetworks", e);
+ Log.e(TAG, "Exception in getHotspotNetworks", e);
}
return List.of();
}
@@ -445,6 +457,8 @@
*
* @return Returns a {@link List} of {@link KnownNetwork} objects, empty list on failure.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@NonNull
public List<KnownNetwork> getKnownNetworks() {
if (mService == null) {
@@ -465,6 +479,8 @@
* @return Returns a {@link SharedConnectivitySettingsState} object with the state, null on
* failure.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Nullable
public SharedConnectivitySettingsState getSettingsState() {
if (mService == null) {
@@ -480,22 +496,24 @@
}
/**
- * Gets the connection status of the tether network the user selected to connect to.
+ * Gets the connection status of the hotspot network the user selected to connect to.
*
- * @return Returns a {@link TetherNetworkConnectionStatus} object with the connection status,
+ * @return Returns a {@link HotspotNetworkConnectionStatus} object with the connection status,
* null on failure. If no connection is active the status will be
- * {@link TetherNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}.
+ * {@link HotspotNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Nullable
- public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() {
+ public HotspotNetworkConnectionStatus getHotspotNetworkConnectionStatus() {
if (mService == null) {
return null;
}
try {
- return mService.getTetherNetworkConnectionStatus();
+ return mService.getHotspotNetworkConnectionStatus();
} catch (RemoteException e) {
- Log.e(TAG, "Exception in getTetherNetworkConnectionStatus", e);
+ Log.e(TAG, "Exception in getHotspotNetworkConnectionStatus", e);
}
return null;
}
@@ -507,6 +525,8 @@
* null on failure. If no connection is active the status will be
* {@link KnownNetworkConnectionStatus#CONNECTION_STATUS_UNKNOWN}.
*/
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Nullable
public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() {
if (mService == null) {
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.aidl
deleted file mode 100644
index c677a6c..0000000
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatus.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2023 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.wifi.sharedconnectivity.app;
-
-parcelable TetherNetworkConnectionStatus;
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
index 6f6f162..737aa6d 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -19,15 +19,15 @@
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
-import android.net.wifi.sharedconnectivity.app.TetherNetwork;
-import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
+import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
+import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
/*
* @hide
*/
interface ISharedConnectivityCallback {
- oneway void onTetherNetworksUpdated(in List<TetherNetwork> networks);
- oneway void onTetherNetworkConnectionStatusChanged(in TetherNetworkConnectionStatus status);
+ oneway void onHotspotNetworksUpdated(in List<HotspotNetwork> networks);
+ oneway void onHotspotNetworkConnectionStatusChanged(in HotspotNetworkConnectionStatus status);
oneway void onKnownNetworksUpdated(in List<KnownNetwork> networks);
oneway void onKnownNetworkConnectionStatusChanged(in KnownNetworkConnectionStatus status);
oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
index 9f33e99..c81380d 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityService.aidl
@@ -17,10 +17,10 @@
package android.net.wifi.sharedconnectivity.service;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
-import android.net.wifi.sharedconnectivity.app.TetherNetwork;
+import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
-import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
+import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.service.ISharedConnectivityCallback;
/*
@@ -29,13 +29,13 @@
interface ISharedConnectivityService {
void registerCallback(in ISharedConnectivityCallback callback);
void unregisterCallback(in ISharedConnectivityCallback callback);
- void connectTetherNetwork(in TetherNetwork network);
- void disconnectTetherNetwork(in TetherNetwork network);
+ void connectHotspotNetwork(in HotspotNetwork network);
+ void disconnectHotspotNetwork(in HotspotNetwork network);
void connectKnownNetwork(in KnownNetwork network);
void forgetKnownNetwork(in KnownNetwork network);
- List<TetherNetwork> getTetherNetworks();
+ List<HotspotNetwork> getHotspotNetworks();
List<KnownNetwork> getKnownNetworks();
SharedConnectivitySettingsState getSettingsState();
- TetherNetworkConnectionStatus getTetherNetworkConnectionStatus();
+ HotspotNetworkConnectionStatus getHotspotNetworkConnectionStatus();
KnownNetworkConnectionStatus getKnownNetworkConnectionStatus();
}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index d2cea61..c53da9c 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -27,12 +27,12 @@
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
+import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
-import android.net.wifi.sharedconnectivity.app.TetherNetwork;
-import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -62,14 +62,14 @@
private Handler mHandler;
private final RemoteCallbackList<ISharedConnectivityCallback> mRemoteCallbackList =
new RemoteCallbackList<>();
- private List<TetherNetwork> mTetherNetworks = Collections.emptyList();
+ private List<HotspotNetwork> mHotspotNetworks = Collections.emptyList();
private List<KnownNetwork> mKnownNetworks = Collections.emptyList();
private SharedConnectivitySettingsState mSettingsState =
new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(false)
.setExtras(Bundle.EMPTY).build();
- private TetherNetworkConnectionStatus mTetherNetworkConnectionStatus =
- new TetherNetworkConnectionStatus.Builder()
- .setStatus(TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
+ private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus =
+ new HotspotNetworkConnectionStatus.Builder()
+ .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN)
.setExtras(Bundle.EMPTY).build();
private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus =
new KnownNetworkConnectionStatus.Builder()
@@ -82,66 +82,147 @@
if (DEBUG) Log.i(TAG, "onBind intent=" + intent);
mHandler = new Handler(getMainLooper());
IBinder serviceStub = new ISharedConnectivityService.Stub() {
+
+ /**
+ * Registers a callback for receiving updates to the list of Hotspot Networks, Known
+ * Networks, shared connectivity settings state, hotspot network connection status and
+ * known network connection status.
+ *
+ * @param callback The callback of type {@link ISharedConnectivityCallback} to be called
+ * when there is update to the data.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public void registerCallback(ISharedConnectivityCallback callback) {
checkPermissions();
mHandler.post(() -> onRegisterCallback(callback));
}
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback to unregister.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public void unregisterCallback(ISharedConnectivityCallback callback) {
checkPermissions();
mHandler.post(() -> onUnregisterCallback(callback));
}
+ /**
+ * Connects to a hotspot network.
+ *
+ * @param network The network to connect to.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
- public void connectTetherNetwork(TetherNetwork network) {
+ public void connectHotspotNetwork(HotspotNetwork network) {
checkPermissions();
- mHandler.post(() -> onConnectTetherNetwork(network));
+ mHandler.post(() -> onConnectHotspotNetwork(network));
}
- @Override
- public void disconnectTetherNetwork(TetherNetwork network) {
+ /**
+ * Disconnects from a previously connected hotspot network.
+ *
+ * @param network The network to disconnect from.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
+ public void disconnectHotspotNetwork(HotspotNetwork network) {
checkPermissions();
- mHandler.post(() -> onDisconnectTetherNetwork(network));
+ mHandler.post(() -> onDisconnectHotspotNetwork(network));
}
+ /**
+ * Adds a known network to the available networks on the device and connects to it.
+ *
+ * @param network The network to connect to.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public void connectKnownNetwork(KnownNetwork network) {
checkPermissions();
mHandler.post(() -> onConnectKnownNetwork(network));
}
+ /**
+ * Removes a known network from the available networks on the device which will also
+ * disconnect the device from the network if it is connected to it.
+ *
+ * @param network The network to forget.
+ */
@Override
public void forgetKnownNetwork(KnownNetwork network) {
checkPermissions();
mHandler.post(() -> onForgetKnownNetwork(network));
}
+ /**
+ * Gets the list of hotspot networks the user can select to connect to.
+ *
+ * @return Returns a {@link List} of {@link HotspotNetwork} objects
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
- public List<TetherNetwork> getTetherNetworks() {
+ public List<HotspotNetwork> getHotspotNetworks() {
checkPermissions();
- return mTetherNetworks;
+ return mHotspotNetworks;
}
+ /**
+ * Gets the list of known networks the user can select to connect to.
+ *
+ * @return Returns a {@link List} of {@link KnownNetwork} objects.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public List<KnownNetwork> getKnownNetworks() {
checkPermissions();
return mKnownNetworks;
}
+ /**
+ * Gets the shared connectivity settings state.
+ *
+ * @return Returns a {@link SharedConnectivitySettingsState} object with the state.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public SharedConnectivitySettingsState getSettingsState() {
checkPermissions();
return mSettingsState;
}
+ /**
+ * Gets the connection status of the hotspot network the user selected to connect to.
+ *
+ * @return Returns a {@link HotspotNetworkConnectionStatus} object with the connection
+ * status.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
- public TetherNetworkConnectionStatus getTetherNetworkConnectionStatus() {
+ public HotspotNetworkConnectionStatus getHotspotNetworkConnectionStatus() {
checkPermissions();
- return mTetherNetworkConnectionStatus;
+ return mHotspotNetworkConnectionStatus;
}
+ /**
+ * Gets the connection status of the known network the user selected to connect to.
+ *
+ * @return Returns a {@link KnownNetworkConnectionStatus} object with the connection
+ * status.
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD})
@Override
public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() {
checkPermissions();
@@ -172,7 +253,8 @@
/** @hide */
@TestApi
- public void onBind() {}
+ public void onBind() {
+ }
private void onRegisterCallback(ISharedConnectivityCallback callback) {
mRemoteCallbackList.register(callback);
@@ -183,23 +265,23 @@
}
/**
- * Implementing application should call this method to provide an up-to-date list of Tether
+ * Implementing application should call this method to provide an up-to-date list of Hotspot
* Networks to be displayed to the user.
*
* This method updates the cached list and notifies all registered callbacks. Any callbacks that
* are inaccessible will be unregistered.
*
- * @param networks The updated list of {@link TetherNetwork} objects.
+ * @param networks The updated list of {@link HotspotNetwork} objects.
*/
- public final void setTetherNetworks(@NonNull List<TetherNetwork> networks) {
- mTetherNetworks = networks;
+ public final void setHotspotNetworks(@NonNull List<HotspotNetwork> networks) {
+ mHotspotNetworks = networks;
int count = mRemoteCallbackList.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
- mRemoteCallbackList.getBroadcastItem(i).onTetherNetworksUpdated(mTetherNetworks);
+ mRemoteCallbackList.getBroadcastItem(i).onHotspotNetworksUpdated(mHotspotNetworks);
} catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in setTetherNetworks", e);
+ if (DEBUG) Log.w(TAG, "Exception in setHotspotNetworks", e);
}
}
mRemoteCallbackList.finishBroadcast();
@@ -236,7 +318,7 @@
* that are inaccessible will be unregistered.
*
* @param settingsState The updated state {@link SharedConnectivitySettingsState}
- * objects.
+ * objects.
*/
public final void setSettingsState(@NonNull SharedConnectivitySettingsState settingsState) {
mSettingsState = settingsState;
@@ -255,23 +337,22 @@
/**
* Implementing application should call this method to provide an up-to-date status of enabling
- * and connecting to the tether network.
+ * and connecting to the hotspot network.
*
- * @param status The updated status {@link TetherNetworkConnectionStatus} of the connection.
- *
+ * @param status The updated status {@link HotspotNetworkConnectionStatus} of the connection.
*/
- public final void updateTetherNetworkConnectionStatus(
- @NonNull TetherNetworkConnectionStatus status) {
- mTetherNetworkConnectionStatus = status;
+ public final void updateHotspotNetworkConnectionStatus(
+ @NonNull HotspotNetworkConnectionStatus status) {
+ mHotspotNetworkConnectionStatus = status;
int count = mRemoteCallbackList.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
mRemoteCallbackList
- .getBroadcastItem(i).onTetherNetworkConnectionStatusChanged(
- mTetherNetworkConnectionStatus);
+ .getBroadcastItem(i).onHotspotNetworkConnectionStatusChanged(
+ mHotspotNetworkConnectionStatus);
} catch (RemoteException e) {
- if (DEBUG) Log.w(TAG, "Exception in updateTetherNetworkConnectionStatus", e);
+ if (DEBUG) Log.w(TAG, "Exception in updateHotspotNetworkConnectionStatus", e);
}
}
mRemoteCallbackList.finishBroadcast();
@@ -282,7 +363,6 @@
* connecting to a known network.
*
* @param status The updated status {@link KnownNetworkConnectionStatus} of the connection.
- *
*/
public final void updateKnownNetworkConnectionStatus(
@NonNull KnownNetworkConnectionStatus status) {
@@ -304,20 +384,20 @@
/**
* Implementing application should implement this method.
*
- * Implementation should initiate a connection to the Tether Network indicated.
+ * Implementation should initiate a connection to the Hotspot Network indicated.
*
- * @param network Object identifying the Tether Network the user has requested a connection to.
+ * @param network Object identifying the Hotspot Network the user has requested a connection to.
*/
- public abstract void onConnectTetherNetwork(@NonNull TetherNetwork network);
+ public abstract void onConnectHotspotNetwork(@NonNull HotspotNetwork network);
/**
* Implementing application should implement this method.
*
- * Implementation should initiate a disconnection from the active Tether Network.
+ * Implementation should initiate a disconnection from the active Hotspot Network.
*
- * @param network Object identifying the Tether Network the user has requested to disconnect.
+ * @param network Object identifying the Hotspot Network the user has requested to disconnect.
*/
- public abstract void onDisconnectTetherNetwork(@NonNull TetherNetwork network);
+ public abstract void onDisconnectHotspotNetwork(@NonNull HotspotNetwork network);
/**
* Implementing application should implement this method.
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java
similarity index 61%
rename from wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java
rename to wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java
index 0844364..1f76b48 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkConnectionStatusTest.java
@@ -18,10 +18,10 @@
import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
-import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT;
-import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -35,14 +35,15 @@
import java.util.Arrays;
/**
- * Unit tests for {@link TetherNetworkConnectionStatus}.
+ * Unit tests for {@link HotspotNetworkConnectionStatus}.
*/
@SmallTest
-public class TetherNetworkConnectionStatusTest {
+public class HotspotNetworkConnectionStatusTest {
private static final long DEVICE_ID = 11L;
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE_TABLET)
+ .setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
private static final String NETWORK_NAME = "TEST_NETWORK";
private static final String HOTSPOT_SSID = "TEST_SSID";
@@ -57,7 +58,7 @@
*/
@Test
public void testParcelOperation() {
- TetherNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
Parcel parcelW = Parcel.obtain();
status.writeToParcel(parcelW, 0);
@@ -67,8 +68,8 @@
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
- TetherNetworkConnectionStatus fromParcel =
- TetherNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
+ HotspotNetworkConnectionStatus fromParcel =
+ HotspotNetworkConnectionStatus.CREATOR.createFromParcel(parcelR);
assertThat(fromParcel).isEqualTo(status);
assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode());
@@ -79,16 +80,16 @@
*/
@Test
public void testEqualsOperation() {
- TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
assertThat(status1).isEqualTo(status2);
- TetherNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
+ HotspotNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder()
.setStatus(CONNECTION_STATUS_TETHERING_TIMEOUT);
assertThat(builder.build()).isNotEqualTo(status1);
builder = buildConnectionStatusBuilder()
- .setTetherNetwork(buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1).build());
+ .setHotspotNetwork(buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1).build());
assertThat(builder.build()).isNotEqualTo(status1);
}
@@ -97,24 +98,24 @@
*/
@Test
public void testGetMethods() {
- TetherNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status = buildConnectionStatusBuilder().build();
assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_ENABLING_HOTSPOT);
- assertThat(status.getTetherNetwork()).isEqualTo(buildTetherNetworkBuilder().build());
+ assertThat(status.getHotspotNetwork()).isEqualTo(buildHotspotNetworkBuilder().build());
assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE);
}
@Test
public void testHashCode() {
- TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
- TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build();
+ HotspotNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build();
assertThat(status1.hashCode()).isEqualTo(status2.hashCode());
}
- private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
- return new TetherNetworkConnectionStatus.Builder()
+ private HotspotNetworkConnectionStatus.Builder buildConnectionStatusBuilder() {
+ return new HotspotNetworkConnectionStatus.Builder()
.setStatus(CONNECTION_STATUS_ENABLING_HOTSPOT)
- .setTetherNetwork(buildTetherNetworkBuilder().build())
+ .setHotspotNetwork(buildHotspotNetworkBuilder().build())
.setExtras(buildBundle());
}
@@ -124,11 +125,11 @@
return bundle;
}
- private TetherNetwork.Builder buildTetherNetworkBuilder() {
- TetherNetwork.Builder builder = new TetherNetwork.Builder()
+ private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
+ HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
.setDeviceId(DEVICE_ID)
- .setDeviceInfo(DEVICE_INFO)
- .setNetworkType(NETWORK_TYPE)
+ .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
+ .setHostNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
.setHotspotBssid(HOTSPOT_BSSID);
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
similarity index 61%
rename from wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
rename to wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
index a50d767..b769270 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
@@ -19,10 +19,10 @@
import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
-import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_WIFI;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_WIFI;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -36,14 +36,15 @@
import java.util.Arrays;
/**
- * Unit tests for {@link TetherNetwork}.
+ * Unit tests for {@link HotspotNetwork}.
*/
@SmallTest
-public class TetherNetworkTest {
+public class HotspotNetworkTest {
private static final long DEVICE_ID = 11L;
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE_TABLET)
+ .setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
private static final String NETWORK_NAME = "TEST_NETWORK";
private static final String HOTSPOT_SSID = "TEST_SSID";
@@ -51,9 +52,10 @@
private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP};
private static final long DEVICE_ID_1 = 111L;
- private static final DeviceInfo DEVICE_INFO_1 = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_PHONE).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 =
+ new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE_PHONE)
+ .setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
private static final int NETWORK_TYPE_1 = NETWORK_TYPE_WIFI;
private static final String NETWORK_NAME_1 = "TEST_NETWORK1";
private static final String HOTSPOT_SSID_1 = "TEST_SSID1";
@@ -65,7 +67,7 @@
*/
@Test
public void testParcelOperation() {
- TetherNetwork network = buildTetherNetworkBuilder().build();
+ HotspotNetwork network = buildHotspotNetworkBuilder().build();
Parcel parcelW = Parcel.obtain();
network.writeToParcel(parcelW, 0);
@@ -75,7 +77,7 @@
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
- TetherNetwork fromParcel = TetherNetwork.CREATOR.createFromParcel(parcelR);
+ HotspotNetwork fromParcel = HotspotNetwork.CREATOR.createFromParcel(parcelR);
assertThat(fromParcel).isEqualTo(network);
assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode());
@@ -86,30 +88,30 @@
*/
@Test
public void testEqualsOperation() {
- TetherNetwork network1 = buildTetherNetworkBuilder().build();
- TetherNetwork network2 = buildTetherNetworkBuilder().build();
+ HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
assertThat(network1).isEqualTo(network2);
- TetherNetwork.Builder builder = buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1);
+ HotspotNetwork.Builder builder = buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
+ builder = buildHotspotNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder().setNetworkType(NETWORK_TYPE_1);
+ builder = buildHotspotNetworkBuilder().setHostNetworkType(NETWORK_TYPE_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder().setNetworkName(NETWORK_NAME_1);
+ builder = buildHotspotNetworkBuilder().setNetworkName(NETWORK_NAME_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
+ builder = buildHotspotNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
+ builder = buildHotspotNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildTetherNetworkBuilder();
- TetherNetwork.Builder builder1 = buildTetherNetworkBuilder();
+ builder = buildHotspotNetworkBuilder();
+ HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder();
Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
assertThat(builder1.build()).isNotEqualTo(builder.build());
@@ -120,13 +122,13 @@
*/
@Test
public void testGetMethods() {
- TetherNetwork network = buildTetherNetworkBuilder().build();
+ HotspotNetwork network = buildHotspotNetworkBuilder().build();
ArraySet<Integer> securityTypes = new ArraySet<>();
Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
assertThat(network.getDeviceId()).isEqualTo(DEVICE_ID);
- assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO);
- assertThat(network.getNetworkType()).isEqualTo(NETWORK_TYPE);
+ assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO);
+ assertThat(network.getHostNetworkType()).isEqualTo(NETWORK_TYPE);
assertThat(network.getNetworkName()).isEqualTo(NETWORK_NAME);
assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID);
assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID);
@@ -135,17 +137,17 @@
@Test
public void testHashCode() {
- TetherNetwork network1 = buildTetherNetworkBuilder().build();
- TetherNetwork network2 = buildTetherNetworkBuilder().build();
+ HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
+ HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
}
- private TetherNetwork.Builder buildTetherNetworkBuilder() {
- TetherNetwork.Builder builder = new TetherNetwork.Builder()
+ private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
+ HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
.setDeviceId(DEVICE_ID)
- .setDeviceInfo(DEVICE_INFO)
- .setNetworkType(NETWORK_TYPE)
+ .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
+ .setHostNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID)
.setHotspotBssid(HOTSPOT_BSSID);
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
index 8a0f21e..2a6046f 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java
@@ -17,10 +17,10 @@
package android.net.wifi.sharedconnectivity.app;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED;
import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVE_FAILED;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -41,9 +41,11 @@
private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
private static final String SSID = "TEST_SSID";
private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName(
+ "TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
private static final String SSID_1 = "TEST_SSID1";
private static final String BUNDLE_KEY = "INT-KEY";
private static final int BUNDLE_VALUE = 1;
@@ -121,8 +123,8 @@
}
private KnownNetwork.Builder buildKnownNetworkBuilder() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
return builder;
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
index 872dd2e..1b9a7eb 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java
@@ -18,10 +18,10 @@
import static android.net.wifi.WifiInfo.SECURITY_TYPE_PSK;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -43,15 +43,17 @@
private static final int NETWORK_SOURCE = NETWORK_SOURCE_NEARBY_SELF;
private static final String SSID = "TEST_SSID";
private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE_TABLET)
+ .setDeviceName("TEST_NAME").setModelName("TEST_MODEL").setConnectionStrength(2)
+ .setBatteryPercentage(50).build();
private static final int NETWORK_SOURCE_1 = NETWORK_SOURCE_CLOUD_SELF;
private static final String SSID_1 = "TEST_SSID1";
private static final int[] SECURITY_TYPES_1 = {SECURITY_TYPE_PSK};
- private static final DeviceInfo DEVICE_INFO_1 = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_PHONE).setDeviceName("TEST_NAME_1")
- .setModelName("TEST_MODEL_1").setConnectionStrength(3).setBatteryPercentage(33).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO1 =
+ new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE_PHONE)
+ .setDeviceName("TEST_NAME_1").setModelName("TEST_MODEL_1")
+ .setConnectionStrength(3).setBatteryPercentage(33).build();
/**
* Verifies parcel serialization/deserialization.
@@ -94,7 +96,7 @@
Arrays.stream(SECURITY_TYPES_1).forEach(builder::addSecurityType);
assertThat(builder.build()).isNotEqualTo(network1);
- builder = buildKnownNetworkBuilder().setDeviceInfo(DEVICE_INFO_1);
+ builder = buildKnownNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
assertThat(builder.build()).isNotEqualTo(network1);
}
@@ -110,7 +112,7 @@
assertThat(network.getNetworkSource()).isEqualTo(NETWORK_SOURCE);
assertThat(network.getSsid()).isEqualTo(SSID);
assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes);
- assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO);
+ assertThat(network.getNetworkProviderInfo()).isEqualTo(NETWORK_PROVIDER_INFO);
}
@Test
@@ -122,8 +124,8 @@
}
private KnownNetwork.Builder buildKnownNetworkBuilder() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
return builder;
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
similarity index 67%
rename from wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
rename to wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
index e6595eb..5de6544 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfoTest.java
@@ -16,8 +16,8 @@
package android.net.wifi.sharedconnectivity.app;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_LAPTOP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_LAPTOP;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_PHONE;
import static com.google.common.truth.Truth.assertThat;
@@ -28,10 +28,10 @@
import org.junit.Test;
/**
- * Unit tests for {@link DeviceInfo}.
+ * Unit tests for {@link NetworkProviderInfo}.
*/
@SmallTest
-public class DeviceInfoTest {
+public class NetworkProviderInfoTest {
private static final int DEVICE_TYPE = DEVICE_TYPE_PHONE;
private static final String DEVICE_NAME = "TEST_NAME";
@@ -50,7 +50,7 @@
*/
@Test
public void testParcelOperation() {
- DeviceInfo info = buildDeviceInfoBuilder().build();
+ NetworkProviderInfo info = buildNetworkProviderInfoBuilder().build();
Parcel parcelW = Parcel.obtain();
info.writeToParcel(parcelW, 0);
@@ -60,7 +60,7 @@
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
- DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcelR);
+ NetworkProviderInfo fromParcel = NetworkProviderInfo.CREATOR.createFromParcel(parcelR);
assertThat(fromParcel).isEqualTo(info);
assertThat(fromParcel.hashCode()).isEqualTo(info.hashCode());
@@ -71,24 +71,25 @@
*/
@Test
public void testEqualsOperation() {
- DeviceInfo info1 = buildDeviceInfoBuilder().build();
- DeviceInfo info2 = buildDeviceInfoBuilder().build();
+ NetworkProviderInfo info1 = buildNetworkProviderInfoBuilder().build();
+ NetworkProviderInfo info2 = buildNetworkProviderInfoBuilder().build();
assertThat(info1).isEqualTo(info2);
- DeviceInfo.Builder builder = buildDeviceInfoBuilder().setDeviceType(DEVICE_TYPE_1);
+ NetworkProviderInfo.Builder builder = buildNetworkProviderInfoBuilder().setDeviceType(
+ DEVICE_TYPE_1);
assertThat(builder.build()).isNotEqualTo(info1);
- builder = buildDeviceInfoBuilder().setDeviceName(DEVICE_NAME_1);
+ builder = buildNetworkProviderInfoBuilder().setDeviceName(DEVICE_NAME_1);
assertThat(builder.build()).isNotEqualTo(info1);
- builder = buildDeviceInfoBuilder().setModelName(DEVICE_MODEL_1);
+ builder = buildNetworkProviderInfoBuilder().setModelName(DEVICE_MODEL_1);
assertThat(builder.build()).isNotEqualTo(info1);
- builder = buildDeviceInfoBuilder()
+ builder = buildNetworkProviderInfoBuilder()
.setBatteryPercentage(BATTERY_PERCENTAGE_1);
assertThat(builder.build()).isNotEqualTo(info1);
- builder = buildDeviceInfoBuilder()
+ builder = buildNetworkProviderInfoBuilder()
.setConnectionStrength(CONNECTION_STRENGTH_1);
assertThat(builder.build()).isNotEqualTo(info1);
}
@@ -98,7 +99,7 @@
*/
@Test
public void testGetMethods() {
- DeviceInfo info = buildDeviceInfoBuilder().build();
+ NetworkProviderInfo info = buildNetworkProviderInfoBuilder().build();
assertThat(info.getDeviceType()).isEqualTo(DEVICE_TYPE);
assertThat(info.getDeviceName()).isEqualTo(DEVICE_NAME);
assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL);
@@ -108,15 +109,16 @@
@Test
public void testHashCode() {
- DeviceInfo info1 = buildDeviceInfoBuilder().build();
- DeviceInfo info2 = buildDeviceInfoBuilder().build();
+ NetworkProviderInfo info1 = buildNetworkProviderInfoBuilder().build();
+ NetworkProviderInfo info2 = buildNetworkProviderInfoBuilder().build();
assertThat(info1.hashCode()).isEqualTo(info2.hashCode());
}
- private DeviceInfo.Builder buildDeviceInfoBuilder() {
- return new DeviceInfo.Builder().setDeviceType(DEVICE_TYPE).setDeviceName(DEVICE_NAME)
- .setModelName(DEVICE_MODEL).setBatteryPercentage(BATTERY_PERCENTAGE)
+ private NetworkProviderInfo.Builder buildNetworkProviderInfoBuilder() {
+ return new NetworkProviderInfo.Builder().setDeviceType(DEVICE_TYPE)
+ .setDeviceName(DEVICE_NAME).setModelName(DEVICE_MODEL)
+ .setBatteryPercentage(BATTERY_PERCENTAGE)
.setConnectionStrength(CONNECTION_STRENGTH);
}
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
index 7c0a8b6..9fc352c 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -18,9 +18,9 @@
import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
-import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -56,9 +56,11 @@
@SmallTest
public class SharedConnectivityManagerTest {
private static final long DEVICE_ID = 11L;
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName(
+ "TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
private static final int NETWORK_TYPE = NETWORK_TYPE_CELLULAR;
private static final String NETWORK_NAME = "TEST_NETWORK";
private static final String HOTSPOT_SSID = "TEST_SSID";
@@ -72,13 +74,16 @@
private static final String SERVICE_INTENT_ACTION = "TEST_INTENT_ACTION";
- @Mock Context mContext;
+ @Mock
+ Context mContext;
@Mock
ISharedConnectivityService mService;
- @Mock Executor mExecutor;
+ @Mock
+ Executor mExecutor;
@Mock
SharedConnectivityClientCallback mClientCallback;
- @Mock Resources mResources;
+ @Mock
+ Resources mResources;
@Mock
ISharedConnectivityService.Stub mIBinder;
@@ -284,69 +289,69 @@
}
/**
- * Verifies connectTetherNetwork behavior.
+ * Verifies connectHotspotNetwork behavior.
*/
@Test
- public void connectTetherNetwork_serviceNotConnected_shouldFail() {
- TetherNetwork network = buildTetherNetwork();
+ public void connectHotspotNetwork_serviceNotConnected_shouldFail() {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertThat(manager.connectTetherNetwork(network)).isFalse();
+ assertThat(manager.connectHotspotNetwork(network)).isFalse();
}
@Test
- public void connectTetherNetwork() throws RemoteException {
- TetherNetwork network = buildTetherNetwork();
+ public void connectHotspotNetwork() throws RemoteException {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- manager.connectTetherNetwork(network);
+ manager.connectHotspotNetwork(network);
- verify(mService).connectTetherNetwork(network);
+ verify(mService).connectHotspotNetwork(network);
}
@Test
- public void connectTetherNetwork_remoteException_shouldFail() throws RemoteException {
- TetherNetwork network = buildTetherNetwork();
+ public void connectHotspotNetwork_remoteException_shouldFail() throws RemoteException {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- doThrow(new RemoteException()).when(mService).connectTetherNetwork(network);
+ doThrow(new RemoteException()).when(mService).connectHotspotNetwork(network);
- assertThat(manager.connectTetherNetwork(network)).isFalse();
+ assertThat(manager.connectHotspotNetwork(network)).isFalse();
}
/**
- * Verifies disconnectTetherNetwork behavior.
+ * Verifies disconnectHotspotNetwork behavior.
*/
@Test
- public void disconnectTetherNetwork_serviceNotConnected_shouldFail() {
- TetherNetwork network = buildTetherNetwork();
+ public void disconnectHotspotNetwork_serviceNotConnected_shouldFail() {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertThat(manager.disconnectTetherNetwork(network)).isFalse();
+ assertThat(manager.disconnectHotspotNetwork(network)).isFalse();
}
@Test
- public void disconnectTetherNetwork() throws RemoteException {
- TetherNetwork network = buildTetherNetwork();
+ public void disconnectHotspotNetwork() throws RemoteException {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- manager.disconnectTetherNetwork(network);
+ manager.disconnectHotspotNetwork(network);
- verify(mService).disconnectTetherNetwork(network);
+ verify(mService).disconnectHotspotNetwork(network);
}
@Test
- public void disconnectTetherNetwork_remoteException_shouldFail() throws RemoteException {
- TetherNetwork network = buildTetherNetwork();
+ public void disconnectHotspotNetwork_remoteException_shouldFail() throws RemoteException {
+ HotspotNetwork network = buildHotspotNetwork();
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any());
+ doThrow(new RemoteException()).when(mService).disconnectHotspotNetwork(any());
- assertThat(manager.disconnectTetherNetwork(network)).isFalse();
+ assertThat(manager.disconnectHotspotNetwork(network)).isFalse();
}
/**
@@ -419,7 +424,7 @@
* Verify getters.
*/
@Test
- public void getTetherNetworks_serviceNotConnected_shouldReturnEmptyList() {
+ public void getHotspotNetworks_serviceNotConnected_shouldReturnEmptyList() {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
@@ -427,22 +432,22 @@
}
@Test
- public void getTetherNetworks_remoteException_shouldReturnEmptyList() throws RemoteException {
+ public void getHotspotNetworks_remoteException_shouldReturnEmptyList() throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getTetherNetworks();
+ doThrow(new RemoteException()).when(mService).getHotspotNetworks();
assertThat(manager.getKnownNetworks()).isEmpty();
}
@Test
- public void getTetherNetworks_shouldReturnNetworksList() throws RemoteException {
+ public void getHotspotNetworks_shouldReturnNetworksList() throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- List<TetherNetwork> networks = List.of(buildTetherNetwork());
+ List<HotspotNetwork> networks = List.of(buildHotspotNetwork());
manager.setService(mService);
- when(mService.getTetherNetworks()).thenReturn(networks);
+ when(mService.getHotspotNetworks()).thenReturn(networks);
- assertThat(manager.getTetherNetworks()).containsExactly(buildTetherNetwork());
+ assertThat(manager.getHotspotNetworks()).containsExactly(buildHotspotNetwork());
}
@Test
@@ -502,35 +507,35 @@
}
@Test
- public void getTetherNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
+ public void getHotspotNetworkConnectionStatus_serviceNotConnected_shouldReturnNull()
throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
+ assertThat(manager.getHotspotNetworkConnectionStatus()).isNull();
}
@Test
- public void getTetherNetworkConnectionStatus_remoteException_shouldReturnNull()
+ public void getHotspotNetworkConnectionStatus_remoteException_shouldReturnNull()
throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- doThrow(new RemoteException()).when(mService).getTetherNetworkConnectionStatus();
+ doThrow(new RemoteException()).when(mService).getHotspotNetworkConnectionStatus();
- assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
+ assertThat(manager.getHotspotNetworkConnectionStatus()).isNull();
}
@Test
- public void getTetherNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
+ public void getHotspotNetworkConnectionStatus_serviceConnected_shouldReturnStatus()
throws RemoteException {
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
- TetherNetworkConnectionStatus status = new TetherNetworkConnectionStatus.Builder()
- .setStatus(TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT)
+ HotspotNetworkConnectionStatus status = new HotspotNetworkConnectionStatus.Builder()
+ .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT)
.setExtras(new Bundle()).build();
manager.setService(mService);
- when(mService.getTetherNetworkConnectionStatus()).thenReturn(status);
+ when(mService.getHotspotNetworkConnectionStatus()).thenReturn(status);
- assertThat(manager.getTetherNetworkConnectionStatus()).isEqualTo(status);
+ assertThat(manager.getHotspotNetworkConnectionStatus()).isEqualTo(status);
}
@Test
@@ -571,11 +576,11 @@
.thenReturn(SERVICE_PACKAGE_NAME, SERVICE_INTENT_ACTION);
}
- private TetherNetwork buildTetherNetwork() {
- TetherNetwork.Builder builder = new TetherNetwork.Builder()
+ private HotspotNetwork buildHotspotNetwork() {
+ HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
.setDeviceId(DEVICE_ID)
- .setDeviceInfo(DEVICE_INFO)
- .setNetworkType(NETWORK_TYPE)
+ .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
+ .setHostNetworkType(NETWORK_TYPE)
.setNetworkName(NETWORK_NAME)
.setHotspotSsid(HOTSPOT_SSID);
Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
@@ -583,8 +588,8 @@
}
private KnownNetwork buildKnownNetwork() {
- KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
- .setSsid(SSID).setDeviceInfo(DEVICE_INFO);
+ KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE)
+ .setSsid(SSID).setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType);
return builder.build();
}
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
index 81efa79..cf437b7 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java
@@ -18,11 +18,11 @@
import static android.net.wifi.WifiInfo.SECURITY_TYPE_EAP;
import static android.net.wifi.WifiInfo.SECURITY_TYPE_WEP;
-import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TABLET;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetwork.NETWORK_TYPE_CELLULAR;
+import static android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN;
import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF;
import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED;
-import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
-import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN;
+import static android.net.wifi.sharedconnectivity.app.NetworkProviderInfo.DEVICE_TYPE_TABLET;
import static com.google.common.truth.Truth.assertThat;
@@ -30,12 +30,12 @@
import android.content.Context;
import android.content.Intent;
-import android.net.wifi.sharedconnectivity.app.DeviceInfo;
+import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
+import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
import android.net.wifi.sharedconnectivity.app.KnownNetwork;
import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState;
-import android.net.wifi.sharedconnectivity.app.TetherNetwork;
-import android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus;
import android.os.Bundle;
import android.os.Looper;
import android.os.RemoteException;
@@ -55,27 +55,31 @@
*/
@SmallTest
public class SharedConnectivityServiceTest {
- private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder()
- .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL")
- .setConnectionStrength(2).setBatteryPercentage(50).build();
- private static final TetherNetwork TETHER_NETWORK =
- new TetherNetwork.Builder().setDeviceId(1).setDeviceInfo(DEVICE_INFO)
- .setNetworkType(NETWORK_TYPE_CELLULAR).setNetworkName("TEST_NETWORK")
+ private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
+ new NetworkProviderInfo.Builder()
+ .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName(
+ "TEST_MODEL")
+ .setConnectionStrength(2).setBatteryPercentage(50).build();
+ private static final HotspotNetwork HOTSPOT_NETWORK =
+ new HotspotNetwork.Builder().setDeviceId(1).setNetworkProviderInfo(
+ NETWORK_PROVIDER_INFO)
+ .setHostNetworkType(NETWORK_TYPE_CELLULAR).setNetworkName("TEST_NETWORK")
.setHotspotSsid("TEST_SSID").setHotspotBssid("TEST_BSSID")
.addHotspotSecurityType(SECURITY_TYPE_WEP)
.addHotspotSecurityType(SECURITY_TYPE_EAP).build();
- private static final List<TetherNetwork> TETHER_NETWORKS = List.of(TETHER_NETWORK);
+ private static final List<HotspotNetwork> HOTSPOT_NETWORKS = List.of(HOTSPOT_NETWORK);
private static final KnownNetwork KNOWN_NETWORK =
new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE_NEARBY_SELF)
.setSsid("TEST_SSID").addSecurityType(SECURITY_TYPE_WEP)
- .addSecurityType(SECURITY_TYPE_EAP).setDeviceInfo(DEVICE_INFO).build();
+ .addSecurityType(SECURITY_TYPE_EAP).setNetworkProviderInfo(
+ NETWORK_PROVIDER_INFO).build();
private static final List<KnownNetwork> KNOWN_NETWORKS = List.of(KNOWN_NETWORK);
private static final SharedConnectivitySettingsState SETTINGS_STATE =
new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true)
.setExtras(Bundle.EMPTY).build();
- private static final TetherNetworkConnectionStatus TETHER_NETWORK_CONNECTION_STATUS =
- new TetherNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_UNKNOWN)
- .setTetherNetwork(TETHER_NETWORK).setExtras(Bundle.EMPTY).build();
+ private static final HotspotNetworkConnectionStatus TETHER_NETWORK_CONNECTION_STATUS =
+ new HotspotNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_UNKNOWN)
+ .setHotspotNetwork(HOTSPOT_NETWORK).setExtras(Bundle.EMPTY).build();
private static final KnownNetworkConnectionStatus KNOWN_NETWORK_CONNECTION_STATUS =
new KnownNetworkConnectionStatus.Builder().setStatus(CONNECTION_STATUS_SAVED)
.setKnownNetwork(KNOWN_NETWORK).setExtras(Bundle.EMPTY).build();
@@ -89,16 +93,20 @@
}
@Override
- public void onConnectTetherNetwork(@NonNull TetherNetwork network) {}
+ public void onConnectHotspotNetwork(@NonNull HotspotNetwork network) {
+ }
@Override
- public void onDisconnectTetherNetwork(@NonNull TetherNetwork network) {}
+ public void onDisconnectHotspotNetwork(@NonNull HotspotNetwork network) {
+ }
@Override
- public void onConnectKnownNetwork(@NonNull KnownNetwork network) {}
+ public void onConnectKnownNetwork(@NonNull KnownNetwork network) {
+ }
@Override
- public void onForgetKnownNetwork(@NonNull KnownNetwork network) {}
+ public void onForgetKnownNetwork(@NonNull KnownNetwork network) {
+ }
}
@Before
@@ -115,15 +123,15 @@
}
@Test
- public void getTetherNetworks() throws RemoteException {
+ public void getHotspotNetworks() throws RemoteException {
SharedConnectivityService service = createService();
ISharedConnectivityService.Stub binder =
(ISharedConnectivityService.Stub) service.onBind(new Intent());
- service.setTetherNetworks(TETHER_NETWORKS);
+ service.setHotspotNetworks(HOTSPOT_NETWORKS);
- assertThat(binder.getTetherNetworks())
- .containsExactlyElementsIn(List.copyOf(TETHER_NETWORKS));
+ assertThat(binder.getHotspotNetworks())
+ .containsExactlyElementsIn(List.copyOf(HOTSPOT_NETWORKS));
}
@Test
@@ -150,14 +158,14 @@
}
@Test
- public void updateTetherNetworkConnectionStatus() throws RemoteException {
+ public void updateHotspotNetworkConnectionStatus() throws RemoteException {
SharedConnectivityService service = createService();
ISharedConnectivityService.Stub binder =
(ISharedConnectivityService.Stub) service.onBind(new Intent());
- service.updateTetherNetworkConnectionStatus(TETHER_NETWORK_CONNECTION_STATUS);
+ service.updateHotspotNetworkConnectionStatus(TETHER_NETWORK_CONNECTION_STATUS);
- assertThat(binder.getTetherNetworkConnectionStatus())
+ assertThat(binder.getHotspotNetworkConnectionStatus())
.isEqualTo(TETHER_NETWORK_CONNECTION_STATUS);
}