Merge "Add CredDescRegistry unit tests" into udc-dev am: e334986d9b am: d2a4acedcc
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21689143
Change-Id: I44343579d901e07480f214c5f46e94b88ae8c623
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 6d89717..7930c01 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20388,7 +20388,7 @@
public final class GnssCapabilities implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.location.GnssSignalType> getGnssSignalTypes();
- method public boolean hasAccumulatedDeltaRange();
+ method public int hasAccumulatedDeltaRange();
method public boolean hasAntennaInfo();
method public boolean hasGeofencing();
method @Deprecated public boolean hasGnssAntennaInfo();
@@ -20414,8 +20414,10 @@
method public boolean hasSatellitePvt();
method public boolean hasScheduling();
method public boolean hasSingleShotFix();
- method public boolean isAccumulatedDeltaRangeCapabilityKnown();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPABILITY_SUPPORTED = 1; // 0x1
+ field public static final int CAPABILITY_UNKNOWN = 0; // 0x0
+ field public static final int CAPABILITY_UNSUPPORTED = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCapabilities> CREATOR;
}
@@ -20423,9 +20425,8 @@
ctor public GnssCapabilities.Builder();
ctor public GnssCapabilities.Builder(@NonNull android.location.GnssCapabilities);
method @NonNull public android.location.GnssCapabilities build();
- method @NonNull public android.location.GnssCapabilities.Builder clearIsAccumulatedDeltaRangeCapabilityKnown();
method @NonNull public android.location.GnssCapabilities.Builder setGnssSignalTypes(@NonNull java.util.List<android.location.GnssSignalType>);
- method @NonNull public android.location.GnssCapabilities.Builder setHasAccumulatedDeltaRange(boolean);
+ method @NonNull public android.location.GnssCapabilities.Builder setHasAccumulatedDeltaRange(int);
method @NonNull public android.location.GnssCapabilities.Builder setHasAntennaInfo(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasGeofencing(boolean);
method @NonNull public android.location.GnssCapabilities.Builder setHasLowPowerMode(boolean);
@@ -32597,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 {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8a6faa1..29976c8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5988,6 +5988,7 @@
method @Nullable public android.media.AudioFormat getCaptureFormat();
method public int getCaptureSession();
method public byte[] getData();
+ method public long getHalEventReceivedMillis();
method public boolean isCaptureAvailable();
}
@@ -13053,6 +13054,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();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1860f43..2f8d0b5 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/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 5cad1ae..ac92811 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -93,7 +93,7 @@
private static final String TAG = "Instrumentation";
- private static final long CONNECT_TIMEOUT_MILLIS = 5000;
+ private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index d3502c5..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/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index ddbfb9e..889a43c 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -40,7 +40,7 @@
* HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object
* representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing
* buffers across different application processes. In particular, HardwareBuffers may be mappable
- * to memory accessibly to various hardware systems, such as the GPU, a sensor or context hub, or
+ * to memory accessible to various hardware systems, such as the GPU, a sensor or context hub, or
* other auxiliary processing units.
*
* For more information, see the NDK documentation for <code>AHardwareBuffer</code>.
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/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index e2af9b0..b749d69 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -420,6 +420,7 @@
// Guarded by NfcAdapter.class
static boolean sIsInitialized = false;
static boolean sHasNfcFeature;
+ static boolean sHasCeFeature;
// Final after first constructor, except for
// attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -616,11 +617,13 @@
PackageManager pm;
pm = context.getPackageManager();
sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
- boolean hasHceFeature =
+ sHasCeFeature =
pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
- || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE);
/* is this device meant to have NFC */
- if (!sHasNfcFeature && !hasHceFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
Log.v(TAG, "this device does not have NFC support");
throw new UnsupportedOperationException();
}
@@ -643,7 +646,7 @@
throw new UnsupportedOperationException();
}
}
- if (hasHceFeature) {
+ if (sHasCeFeature) {
try {
sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface();
} catch (RemoteException e) {
@@ -1669,7 +1672,7 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean enableSecureNfc(boolean enable) {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1694,10 +1697,13 @@
* Checks if the device supports Secure NFC functionality.
*
* @return True if device supports Secure NFC, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
*/
public boolean isSecureNfcSupported() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1723,11 +1729,14 @@
* such as their relative positioning on the device.
*
* @return Information on the nfc antenna(s) on the device.
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
*/
@Nullable
public NfcAntennaInfo getNfcAntennaInfo() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -1752,12 +1761,15 @@
* Checks Secure NFC feature is enabled.
*
* @return True if Secure NFC is enabled, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @throws UnsupportedOperationException if device doesn't support
* Secure NFC functionality. {@link #isSecureNfcSupported}
*/
public boolean isSecureNfcEnabled() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -2071,14 +2083,17 @@
* always on.
* @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
* disabled), if false the NFCC will follow completely the Nfc adapter state.
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @return void
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
public boolean setControllerAlwaysOn(boolean value) {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
@@ -2103,7 +2118,10 @@
* Checks NFC controller always on feature is enabled.
*
* @return True if NFC controller always on is enabled, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @hide
*/
@SystemApi
@@ -2131,13 +2149,16 @@
* Checks if the device supports NFC controller always on functionality.
*
* @return True if device supports NFC controller always on, false otherwise
- * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if FEATURE_NFC,
+ * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF,
+ * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE
+ * are unavailable
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
public boolean isControllerAlwaysOnSupported() {
- if (!sHasNfcFeature) {
+ if (!sHasNfcFeature && !sHasCeFeature) {
throw new UnsupportedOperationException();
}
try {
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 290f929..08aea8e 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/provider/Settings.java b/core/java/android/provider/Settings.java
index ef00774..ac78fbc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4385,6 +4385,16 @@
= "wear_accessibility_gesture_enabled";
/**
+ * If the triple press gesture for toggling accessibility is enabled during OOBE.
+ * Set to 1 for true and 0 for false.
+ *
+ * This setting is used only internally.
+ * @hide
+ */
+ public static final String WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE =
+ "wear_accessibility_gesture_enabled_during_oobe";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
*/
@Deprecated
@@ -5766,6 +5776,7 @@
PRIVATE_SETTINGS.add(END_BUTTON_BEHAVIOR);
PRIVATE_SETTINGS.add(ADVANCED_SETTINGS);
PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED);
+ PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE);
PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ);
PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
PRIVATE_SETTINGS.add(VOLUME_MASTER);
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index b1dc686..4687855 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,20 @@
}
/**
+ * 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 mHalEventReceivedMillis;
+ }
+
+ /**
* Builder class for {@link EventPayload} objects
*
* @hide
@@ -561,6 +582,8 @@
private HotwordDetectedResult mHotwordDetectedResult = null;
private ParcelFileDescriptor mAudioStream = null;
private List<KeyphraseRecognitionExtra> mKeyphraseExtras = Collections.emptyList();
+ @ElapsedRealtimeLong
+ private long mHalEventReceivedMillis = -1;
public Builder() {}
@@ -579,6 +602,7 @@
setKeyphraseRecognitionExtras(
Arrays.asList(keyphraseRecognitionEvent.keyphraseExtras));
}
+ setHalEventReceivedMillis(keyphraseRecognitionEvent.getHalEventReceivedMillis());
}
/**
@@ -682,13 +706,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 +943,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 +958,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/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/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java
index 8761b74..4911346 100644
--- a/core/java/com/android/internal/app/procstats/UidState.java
+++ b/core/java/com/android/internal/app/procstats/UidState.java
@@ -150,6 +150,7 @@
public void resetSafely(long now) {
mDurations.resetTable();
mStartTime = now;
+ mProcesses.removeIf(p -> !p.isInUse());
}
/**
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 b86020e..6fc6dc1 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1075,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/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 2a67b44..a21009f2f 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/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/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
index 9b9a84b..35b3267 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -156,4 +156,19 @@
eq(0),
eq(APP_1_PROCESS_NAME));
}
+
+ @SmallTest
+ public void testSafelyResetClearsProcessInUidState() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ ProcessState processState =
+ processStats.getProcessStateLocked(
+ APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+ processState.makeActive();
+ UidState uidState = processStats.mUidStates.get(APP_1_UID);
+ assertTrue(uidState.isInUse());
+ processState.makeInactive();
+ uidState.resetSafely(NOW_MS);
+ processState.makeActive();
+ assertFalse(uidState.isInUse());
+ }
}
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/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/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
new file mode 100644
index 0000000..d01a0ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.content.Context
+import android.system.helpers.CommandsHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.wm.shell.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.IFlickerTestData
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import org.junit.Assume
+import org.junit.Before
+import org.junit.runners.Parameterized
+
+abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
+ protected val context: Context = instrumentation.context
+ protected val letterboxApp = LetterboxAppHelper(instrumentation)
+ lateinit var cmdHelper: CommandsHelper
+ lateinit var letterboxStyle: HashMap<String, String>
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ setStartRotation()
+ letterboxApp.launchViaIntent(wmHelper)
+ setEndRotation()
+ }
+ }
+
+ @Before
+ fun before() {
+ cmdHelper = CommandsHelper.getInstance(instrumentation)
+ Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
+ }
+
+ private fun mapLetterboxStyle(): HashMap<String, String> {
+ val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
+ val lines = res.lines()
+ val map = HashMap<String, String>()
+ for (line in lines) {
+ val keyValuePair = line.split(":")
+ if (keyValuePair.size == 2) {
+ val key = keyValuePair[0].trim()
+ map[key] = keyValuePair[1].trim()
+ }
+ }
+ return map
+ }
+
+ private fun isIgnoreOrientationRequest(): Boolean {
+ val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
+ return res != null && res.contains("true")
+ }
+
+ fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
+
+ fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
+
+ /** Checks that app entering letterboxed state have rounded corners */
+ fun assertLetterboxAppAtStartHasRoundedCorners() {
+ assumeLetterboxRoundedCornersEnabled()
+ flicker.assertLayersStart { this.hasRoundedCorners(letterboxApp) }
+ }
+
+ fun assertLetterboxAppAtEndHasRoundedCorners() {
+ assumeLetterboxRoundedCornersEnabled()
+ flicker.assertLayersEnd { this.hasRoundedCorners(letterboxApp) }
+ }
+
+ /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
+ private fun assumeLetterboxRoundedCornersEnabled() {
+ if (!::letterboxStyle.isInitialized) {
+ letterboxStyle = mapLetterboxStyle()
+ }
+ Assume.assumeTrue(letterboxStyle.getValue("Corner radius") != "0")
+ }
+
+ fun assertLetterboxAppVisibleAtStartAndEnd() {
+ flicker.appWindowIsVisibleAtStart(letterboxApp)
+ flicker.appWindowIsVisibleAtEnd(letterboxApp)
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
new file mode 100644
index 0000000..c57100e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
+ *
+ * Actions:
+ * ```
+ * Rotate non resizable portrait only app to opposite orientation to trigger size compat mode
+ * ```
+ * Notes:
+ * ```
+ * Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [BaseTest]
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ setStartRotation()
+ letterboxApp.launchViaIntent(wmHelper)
+ }
+ transitions { setEndRotation() }
+ teardown { letterboxApp.exit(wmHelper) }
+ }
+
+ /**
+ * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
+ * focus returns to [letterboxApp]
+ */
+ @Postsubmit
+ @Test
+ fun letterboxAppFocusedAtEnd() = flicker.assertEventLog { focusChanges(letterboxApp.`package`) }
+
+ @Postsubmit
+ @Test
+ fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()
+
+ /**
+ * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+ * flicker, and disappears before the transition is complete
+ */
+ @Postsubmit
+ @Test
+ fun rotationLayerAppearsAndVanishes() {
+ flicker.assertLayers {
+ this.isVisible(letterboxApp)
+ .then()
+ .isVisible(ComponentNameMatcher.ROTATION)
+ .then()
+ .isVisible(letterboxApp)
+ .isInvisible(ComponentNameMatcher.ROTATION)
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
new file mode 100644
index 0000000..f111a8d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.helpers.WindowUtils
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test restarting app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
+ *
+ * Actions:
+ * ```
+ * Rotate app to opposite orientation to trigger size compat mode
+ * Press restart button and wait for letterboxed app to resize
+ * ```
+ * Notes:
+ * ```
+ * Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [BaseTest]
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ transitions { letterboxApp.clickRestart(wmHelper) }
+ teardown { letterboxApp.exit(wmHelper) }
+ }
+
+ @Postsubmit
+ @Test
+ fun appVisibleAtStartAndEnd() = assertLetterboxAppVisibleAtStartAndEnd()
+
+ @Postsubmit
+ @Test
+ fun appLayerVisibilityChanges() {
+ flicker.assertLayers {
+ this.isVisible(letterboxApp)
+ .then()
+ .isInvisible(letterboxApp)
+ .then()
+ .isVisible(letterboxApp)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtStartHasRoundedCorners()
+
+ /** Checks that the visible region of [letterboxApp] is still within display bounds */
+ @Postsubmit
+ @Test
+ fun appWindowRemainInsideVisibleBounds() {
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
+ flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 85b2fbc..8eb41b4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -34,6 +35,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 270677470)
class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition { transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } }
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/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index c6f32c2..88f00dc 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -123,6 +123,21 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SubHalPowerCapabilityFlags {}
+ /** The capability is unknown to be supported or not. */
+ public static final int CAPABILITY_UNKNOWN = 0;
+ /** The capability is supported. */
+ public static final int CAPABILITY_SUPPORTED = 1;
+ /** The capability is not supported. */
+ public static final int CAPABILITY_UNSUPPORTED = 2;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = {"CAPABILITY_"}, value = {CAPABILITY_UNKNOWN,
+ CAPABILITY_SUPPORTED,
+ CAPABILITY_UNSUPPORTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CapabilitySupportType {}
+
+
/**
* Returns an empty GnssCapabilities object.
*
@@ -375,30 +390,25 @@
}
/**
- * Returns {@code true} if GNSS chipset supports accumulated delta range, {@code false}
- * otherwise.
- *
- * <p>The value is only known if {@link #isAccumulatedDeltaRangeCapabilityKnown()} is
- * true.
+ * Returns {@link #CAPABILITY_SUPPORTED} if GNSS chipset supports accumulated delta
+ * range, {@link #CAPABILITY_UNSUPPORTED} if GNSS chipset does not support accumulated
+ * delta range, and {@link #CAPABILITY_UNKNOWN} if it is unknown, which means GNSS
+ * chipset may or may not support accumulated delta range.
*
* <p>The accumulated delta range information can be queried in
* {@link android.location.GnssMeasurement#getAccumulatedDeltaRangeState()},
* {@link android.location.GnssMeasurement#getAccumulatedDeltaRangeMeters()}, and
* {@link android.location.GnssMeasurement#getAccumulatedDeltaRangeUncertaintyMeters()}.
*/
- public boolean hasAccumulatedDeltaRange() {
+ public @CapabilitySupportType int hasAccumulatedDeltaRange() {
if (!mIsAdrCapabilityKnown) {
- throw new IllegalStateException("Accumulated delta range capability is unknown.");
+ return CAPABILITY_UNKNOWN;
}
- return (mTopFlags & TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE) != 0;
- }
-
- /**
- * Returns {@code true} if {@link #hasAccumulatedDeltaRange()} is known, {@code false}
- * otherwise.
- */
- public boolean isAccumulatedDeltaRangeCapabilityKnown() {
- return mIsAdrCapabilityKnown;
+ if ((mTopFlags & TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE) != 0) {
+ return CAPABILITY_SUPPORTED;
+ } else {
+ return CAPABILITY_UNSUPPORTED;
+ }
}
/**
@@ -597,9 +607,9 @@
if (hasMeasurementCorrectionsForDriving()) {
builder.append("MEASUREMENT_CORRECTIONS_FOR_DRIVING ");
}
- if (mIsAdrCapabilityKnown && hasAccumulatedDeltaRange()) {
+ if (hasAccumulatedDeltaRange() == CAPABILITY_SUPPORTED) {
builder.append("ACCUMULATED_DELTA_RANGE ");
- } else if (!mIsAdrCapabilityKnown) {
+ } else if (hasAccumulatedDeltaRange() == CAPABILITY_UNKNOWN) {
builder.append("ACCUMULATED_DELTA_RANGE(unknown) ");
}
if (hasMeasurementCorrectionsLosSats()) {
@@ -795,19 +805,17 @@
/**
* Sets accumulated delta range capability.
*/
- public @NonNull Builder setHasAccumulatedDeltaRange(boolean capable) {
- mIsAdrCapabilityKnown = true;
- mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE,
- capable);
- return this;
- }
-
- /**
- * Clears accumulated delta range capability and sets it as unknown.
- */
- public @NonNull Builder clearIsAccumulatedDeltaRangeCapabilityKnown() {
- mIsAdrCapabilityKnown = false;
- mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE, false);
+ public @NonNull Builder setHasAccumulatedDeltaRange(@CapabilitySupportType int capable) {
+ if (capable == CAPABILITY_UNKNOWN) {
+ mIsAdrCapabilityKnown = false;
+ mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE, false);
+ } else if (capable == CAPABILITY_SUPPORTED) {
+ mIsAdrCapabilityKnown = true;
+ mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE, true);
+ } else if (capable == CAPABILITY_UNSUPPORTED) {
+ mIsAdrCapabilityKnown = true;
+ mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ACCUMULATED_DELTA_RANGE, false);
+ }
return this;
}
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/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 @@
hanxu@google.com
kellyz@google.com
pierreqian@google.com
+lijun@google.com
+songchenxi@google.com
+cyl@google.com
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..a9d15f3 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,
+ SelectionBehavior.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/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c2a3ada..a631c81 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -211,6 +211,7 @@
VALIDATORS.put(System.SHOW_BATTERY_PERCENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.UNREAD_NOTIFICATION_DOT_INDICATOR, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.AUTO_LAUNCH_MEDIA_CONTROLS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 278ceb9..ec611cf 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -100,6 +100,7 @@
Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
);
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/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/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/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index fb2c02a..3af9ea3 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -629,7 +629,7 @@
"userId: $int1 " +
"old: $bool1, " +
"new: $bool2 " +
- "context: $context"
+ "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/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 3608b91..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
@@ -600,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)
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/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 00e9a79..9928c4f 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,12 @@
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 +291,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 +340,7 @@
updateDeviceStatusIcon(deviceStatusIcon);
mStatusIcon.setVisibility(View.VISIBLE);
}
- updateTwoLineLayoutContentAlpha(
+ updateSingleLineLayoutContentAlpha(
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
} else {
@@ -367,11 +364,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 +383,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 +398,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 +423,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 +453,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);
}
@@ -534,11 +537,12 @@
@DoNotInline
static Drawable getDeviceStatusIconBasedOnSelectionBehavior(MediaDevice device,
Context context) {
- switch (device.getSubtext()) {
- case SUBTEXT_AD_ROUTING_DISALLOWED:
- case SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED:
+ switch (device.getSelectionBehavior()) {
+ case SELECTION_BEHAVIOR_NONE:
return context.getDrawable(R.drawable.media_output_status_failed);
- case SUBTEXT_SUBSCRIPTION_REQUIRED:
+ case SELECTION_BEHAVIOR_TRANSFER:
+ return null;
+ case SELECTION_BEHAVIOR_GO_TO_APP:
return context.getDrawable(R.drawable.media_output_status_help);
}
return null;
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/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/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/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/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
-bryanmawhinney@google.com
jstemmer@google.com
martinoh@google.com
millmore@google.com
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/Android.bp b/services/core/Android.bp
index c8caab9..199fc22 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -74,8 +74,8 @@
"{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " +
"{ echo -e '\\n\\n################################################################\\n#\\n" +
"# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
- "# cp $(location :generate-protolog.json) " +
- "$(location :services.core.protolog.json)\\n#\\n" +
+ "# cp $${ANDROID_BUILD_TOP}/$(location :generate-protolog.json) " +
+ "$${ANDROID_BUILD_TOP}/$(location :services.core.protolog.json)\\n#\\n" +
"################################################################\\n\\n' >&2 && false; } }",
out: ["services.core.protolog.json"],
}
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/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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 432c209..ccc218e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15574,16 +15574,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) {
@@ -15592,12 +15582,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;
@@ -15631,7 +15629,7 @@
ProcessRecord app = addAppLocked(
sdkSandboxInfo,
- processName,
+ sdkSandboxInfo.processName,
/* isolated= */ false,
/* isSdkSandbox= */ true,
sdkSandboxUid,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 02c1b8b..2c79f8c 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/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/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 4f28432..0476f2c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2141,17 +2141,6 @@
// 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) {
@@ -2768,7 +2757,7 @@
*
* 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")
@@ -2785,9 +2774,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);
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/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/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b6557d0..4675a7c 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;
@@ -867,6 +868,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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2f98d34..1a486dc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2422,41 +2422,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 {
@@ -5407,23 +5424,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
@@ -5470,45 +5501,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.
@@ -5577,6 +5579,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) {
@@ -5586,65 +5589,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);
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45dc49d..92dd95a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -68,6 +68,7 @@
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -190,6 +191,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
@@ -1400,7 +1402,18 @@
if (DEBUG_INPUT) {
Slog.d(TAG, "Executing stem primary triple press action behavior.");
}
- toggleTalkBack();
+
+ if (Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+ /* def= */ 0, UserHandle.USER_CURRENT) == 1) {
+ /** Toggle talkback begin */
+ ComponentName componentName = getTalkbackComponent();
+ if (componentName != null && toggleTalkBack(componentName)) {
+ /** log stem triple press telemetry if it's a talkback enabled event */
+ logStemTriplePressAccessibilityTelemetry(componentName);
+ }
+ /** Toggle talkback end */
+ }
break;
}
}
@@ -1419,17 +1432,39 @@
}
}
- private void toggleTalkBack() {
- final ComponentName componentName = getTalkbackComponent();
- if (componentName == null) {
- return;
- }
-
+ /**
+ * A function that toggles talkback service
+ *
+ * @return {@code true} if talkback is enabled, {@code false} if talkback is disabled
+ */
+ private boolean toggleTalkBack(ComponentName componentName) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId);
+ boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName);
AccessibilityUtils.setAccessibilityServiceState(mContext, componentName,
- !enabledServices.contains(componentName));
+ !isTalkbackAlreadyEnabled);
+ /** if isTalkbackAlreadyEnabled is true, then it's a disabled event so return false
+ * and if isTalkbackAlreadyEnabled is false, return true as it's an enabled event */
+ return !isTalkbackAlreadyEnabled;
+ }
+
+ /**
+ * A function that logs stem triple press accessibility telemetry
+ * If the user setup (Oobe) is not completed, set the
+ * WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE
+ * setting which will be later logged via Settings Snapshot
+ * else, log ACCESSIBILITY_SHORTCUT_REPORTED atom
+ */
+ private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) {
+ if (!AccessibilityUtils.isUserSetupCompleted(mContext)) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1);
+ } else {
+ AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, componentName,
+ ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE,
+ /* serviceEnabled= */ true);
+ }
}
private ComponentName getTalkbackComponent() {
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 85f1357..646dc4e 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.EventLogTags;
import java.io.PrintWriter;
@@ -255,6 +256,11 @@
public void setOccluded(boolean isOccluded, boolean notify) {
if (mKeyguardService != null && notify) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "setOccluded");
mKeyguardService.setOccluded(isOccluded, false /* animate */);
}
mKeyguardState.occluded = isOccluded;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 2e8a150..ea494db 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -6644,6 +6644,13 @@
}
}
+ @VisibleForTesting
+ int getPowerGroupSize() {
+ synchronized (mLock) {
+ return mPowerGroups.size();
+ }
+ }
+
@GoToSleepReason
private int getLastSleepReasonInternal() {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 5096ad1..a5ca7ac 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -17,6 +17,7 @@
package com.android.server.power;
+import android.app.ActivityManagerInternal;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
@@ -25,10 +26,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
-import android.media.AudioAttributes;
+import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -41,6 +43,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;
@@ -51,7 +55,6 @@
import com.android.server.LocalServices;
import com.android.server.RescueParty;
-import com.android.server.pm.PackageManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.File;
@@ -99,11 +102,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<>();
@@ -448,13 +446,6 @@
new File(CHECK_POINTS_FILE_BASENAME));
dumpCheckPointsThread.start();
- BroadcastReceiver br = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
- // We don't allow apps to cancel this, so ignore the result.
- actionDone();
- }
- };
-
/*
* Write a system property in case the system_server reboots before we
* get to the actual hardware restart. If that happens, we'll retry at
@@ -490,8 +481,16 @@
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendOrderedBroadcastAsUser(intent,
- UserHandle.ALL, null, br, mHandler, 0, null, null);
+ final ActivityManagerInternal activityManagerInternal = LocalServices.getService(
+ ActivityManagerInternal.class);
+ activityManagerInternal.broadcastIntentWithCallback(intent,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ mHandler.post(ShutdownThread.this::actionDone);
+ }
+ }, null, UserHandle.USER_ALL, null, null, null);
final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
synchronized (mActionDoneSync) {
@@ -700,7 +699,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/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 594929b..244656c 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -49,7 +49,7 @@
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
# Keyguard status changed
-30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
+30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(occluded|1),(Reason|3)
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
@@ -65,6 +65,8 @@
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
+# Notify keyguard occlude statuc change to SysUI.
+31008 wm_set_keyguard_occluded (occluded|1),(animate|1),(transit|1),(Channel|3)
# Back navigation.
31100 wm_back_navi_canceled (Reason|3)
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 32b3ccf..08a6358 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -188,6 +188,7 @@
keyguardShowing ? 1 : 0,
aodShowing ? 1 : 0,
state.mKeyguardGoingAway ? 1 : 0,
+ state.mOccluded ? 1 : 0,
"setKeyguardShown");
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -253,9 +254,10 @@
try {
EventLogTags.writeWmSetKeyguardShown(
displayId,
- 1 /* keyguardShowing */,
+ state.mKeyguardShowing ? 1 : 0,
state.mAodShowing ? 1 : 0,
1 /* keyguardGoingAway */,
+ state.mOccluded ? 1 : 0,
"keyguardGoingAway");
final int transitFlags = convertTransitFlags(flags);
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
@@ -669,6 +671,15 @@
boolean hasChange = false;
if (lastOccluded != mOccluded) {
+ if (mDisplayId == DEFAULT_DISPLAY) {
+ EventLogTags.writeWmSetKeyguardShown(
+ mDisplayId,
+ mKeyguardShowing ? 1 : 0,
+ mAodShowing ? 1 : 0,
+ mKeyguardGoingAway ? 1 : 0,
+ mOccluded ? 1 : 0,
+ "updateVisibility");
+ }
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
hasChange = true;
} else if (!lastKeyguardGoingAway && mKeyguardGoingAway) {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 3635ebb..b879d1a 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
@@ -195,6 +197,13 @@
+ " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
AppTransition.appTransitionOldToString(transit), appTargets.length,
wallpaperTargets.length, nonAppTargets.length);
+ if (AppTransition.isKeyguardOccludeTransitOld(transit)) {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE ? 0 : 1,
+ 1 /* animate */,
+ transit,
+ "onAnimationStart");
+ }
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
@@ -356,6 +365,11 @@
final boolean isKeyguardOccluded = mDisplayContent.isKeyguardOccluded();
try {
+ EventLogTags.writeWmSetKeyguardOccluded(
+ isKeyguardOccluded ? 1 : 0,
+ 0 /* animate */,
+ 0 /* transit */,
+ "onAnimationCancelled");
mRemoteAnimationAdapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify cancel", e);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a30ab11..5e8b115 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;
@@ -2239,6 +2240,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 b9cb59a..87da85e 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;
@@ -541,9 +541,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;
@@ -679,7 +679,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.
@@ -731,7 +731,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/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/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/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..f453e79 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -21,9 +21,9 @@
import com.android.modules.utils.BinaryXmlSerializer
import com.android.server.SystemConfig
import com.android.server.permission.access.appop.PackageAppOpPolicy
-import com.android.server.permission.access.appop.UidAppOpPolicy
+import com.android.server.permission.access.appop.AppIdAppOpPolicy
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.permission.UidPermissionPolicy
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.util.forEachTag
import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
@@ -37,8 +37,8 @@
IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply {
fun addPolicy(policy: SchemePolicy) =
getOrPut(policy.subjectScheme) { IndexedMap() }.put(policy.objectScheme, policy)
- addPolicy(UidPermissionPolicy())
- addPolicy(UidAppOpPolicy())
+ addPolicy(AppIdPermissionPolicy())
+ addPolicy(AppIdAppOpPolicy())
addPolicy(PackageAppOpPolicy())
}
)
@@ -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/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 5532311..0b4d6e9 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -98,9 +98,9 @@
class UserState private constructor(
// A map of (appId to a map of (permissionName to permissionFlags))
- val uidPermissionFlags: IntMap<IndexedMap<String, Int>>,
+ val appIdPermissionFlags: IntMap<IndexedMap<String, Int>>,
// appId -> opName -> opCode
- val uidAppOpModes: IntMap<IndexedMap<String, Int>>,
+ val appIdAppOpModes: IntMap<IndexedMap<String, Int>>,
// packageName -> opName -> opCode
val packageAppOpModes: IndexedMap<String, IndexedMap<String, Int>>
) : WritableState() {
@@ -111,8 +111,8 @@
)
fun copy(): UserState = UserState(
- uidPermissionFlags.copy { it.copy { it } },
- uidAppOpModes.copy { it.copy { it } },
+ appIdPermissionFlags.copy { it.copy { it } },
+ appIdAppOpModes.copy { it.copy { it } },
packageAppOpModes.copy { it.copy { it } }
)
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
similarity index 79%
rename from services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt
rename to services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
index 7a965d4..c29f30c8 100644
--- a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
@@ -28,15 +28,15 @@
import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
-class UidAppOpPersistence : BaseAppOpPersistence() {
+class AppIdAppOpPersistence : BaseAppOpPersistence() {
override fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
when (tagName) {
- TAG_UID_APP_OPS -> parseUidAppOps(state, userId)
+ TAG_APP_ID_APP_OPS -> parseAppIdAppOps(state, userId)
else -> {}
}
}
- private fun BinaryXmlPullParser.parseUidAppOps(state: AccessState, userId: Int) {
+ private fun BinaryXmlPullParser.parseAppIdAppOps(state: AccessState, userId: Int) {
val userState = state.userStates[userId]
forEachTag {
when (tagName) {
@@ -44,7 +44,7 @@
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
}
}
- userState.uidAppOpModes.retainAllIndexed { _, appId, _ ->
+ userState.appIdAppOpModes.retainAllIndexed { _, appId, _ ->
val hasAppId = appId in state.systemState.appIds
if (!hasAppId) {
Log.w(LOG_TAG, "Dropping unknown app ID $appId when parsing app-op state")
@@ -56,17 +56,17 @@
private fun BinaryXmlPullParser.parseAppId(userState: UserState) {
val appId = getAttributeIntOrThrow(ATTR_ID)
val appOpModes = IndexedMap<String, Int>()
- userState.uidAppOpModes[appId] = appOpModes
+ userState.appIdAppOpModes[appId] = appOpModes
parseAppOps(appOpModes)
}
override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- serializeUidAppOps(state.userStates[userId])
+ serializeAppIdAppOps(state.userStates[userId])
}
- private fun BinaryXmlSerializer.serializeUidAppOps(userState: UserState) {
- tag(TAG_UID_APP_OPS) {
- userState.uidAppOpModes.forEachIndexed { _, appId, appOpModes ->
+ private fun BinaryXmlSerializer.serializeAppIdAppOps(userState: UserState) {
+ tag(TAG_APP_ID_APP_OPS) {
+ userState.appIdAppOpModes.forEachIndexed { _, appId, appOpModes ->
serializeAppId(appId, appOpModes)
}
}
@@ -83,10 +83,10 @@
}
companion object {
- private val LOG_TAG = UidAppOpPersistence::class.java.simpleName
+ private val LOG_TAG = AppIdAppOpPersistence::class.java.simpleName
private const val TAG_APP_ID = "app-id"
- private const val TAG_UID_APP_OPS = "uid-app-ops"
+ private const val TAG_APP_ID_APP_OPS = "app-id-app-ops"
private const val ATTR_ID = "id"
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
similarity index 90%
rename from services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt
rename to services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
index 0ba9a1e..5a2522e 100644
--- a/services/permission/java/com/android/server/permission/access/appop/UidAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
@@ -24,7 +24,7 @@
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
-class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) {
+class AppIdAppOpPolicy : BaseAppOpPolicy(AppIdAppOpPersistence()) {
@Volatile
private var onAppOpModeChangedListeners = IndexedListSet<OnAppOpModeChangedListener>()
private val onAppOpModeChangedListenersLock = Any()
@@ -54,18 +54,18 @@
override fun MutateStateScope.onAppIdRemoved(appId: Int) {
newState.userStates.forEachIndexed { _, _, userState ->
- userState.uidAppOpModes -= appId
+ userState.appIdAppOpModes -= appId
userState.requestWrite()
// Skip notifying the change listeners since the app ID no longer exists.
}
}
fun GetStateScope.getAppOpModes(appId: Int, userId: Int): IndexedMap<String, Int>? =
- state.userStates[userId].uidAppOpModes[appId]
+ state.userStates[userId].appIdAppOpModes[appId]
fun MutateStateScope.removeAppOpModes(appId: Int, userId: Int): Boolean {
val userState = newState.userStates[userId]
- val isChanged = userState.uidAppOpModes.removeReturnOld(appId) != null
+ val isChanged = userState.appIdAppOpModes.removeReturnOld(appId) != null
if (isChanged) {
userState.requestWrite()
}
@@ -73,7 +73,7 @@
}
fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
- state.userStates[userId].uidAppOpModes[appId]
+ state.userStates[userId].appIdAppOpModes[appId]
.getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))
fun MutateStateScope.setAppOpMode(
@@ -83,8 +83,8 @@
mode: Int
): Boolean {
val userState = newState.userStates[userId]
- val uidAppOpModes = userState.uidAppOpModes
- var appOpModes = uidAppOpModes[appId]
+ val appIdAppOpModes = userState.appIdAppOpModes
+ var appOpModes = appIdAppOpModes[appId]
val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
val oldMode = appOpModes.getWithDefault(appOpName, defaultMode)
if (oldMode == mode) {
@@ -92,11 +92,11 @@
}
if (appOpModes == null) {
appOpModes = IndexedMap()
- uidAppOpModes[appId] = appOpModes
+ appIdAppOpModes[appId] = appOpModes
}
appOpModes.putWithDefault(appOpName, mode, defaultMode)
if (appOpModes.isEmpty()) {
- uidAppOpModes -= appId
+ appIdAppOpModes -= appId
}
userState.requestWrite()
onAppOpModeChangedListeners.forEachIndexed { _, it ->
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index af85eba..4caf6cc 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -47,7 +47,7 @@
private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME)
as PackageAppOpPolicy
private val uidPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
- as UidAppOpPolicy
+ as AppIdAppOpPolicy
private val context = service.context
private lateinit var handler: Handler
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
new file mode 100644
index 0000000..0a2fad9
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.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 AppIdPermissionMigration {
+ 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].appIdPermissionFlags.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 = AppIdPermissionMigration::class.java.simpleName
+
+ private const val DEBUG_MIGRATION = false
+ }
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
similarity index 86%
rename from services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt
rename to services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
index 35cdbce..552dda1 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
@@ -37,7 +37,7 @@
import com.android.server.permission.access.util.tag
import com.android.server.permission.access.util.tagName
-class UidPermissionPersistence {
+class AppIdPermissionPersistence {
fun BinaryXmlPullParser.parseSystemState(state: AccessState) {
val systemState = state.systemState
when (tagName) {
@@ -126,12 +126,12 @@
fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {
when (tagName) {
- TAG_PERMISSIONS -> parsePermissionFlags(state, userId)
+ TAG_APP_ID_PERMISSIONS -> parseAppIdPermissions(state, userId)
else -> {}
}
}
- private fun BinaryXmlPullParser.parsePermissionFlags(state: AccessState, userId: Int) {
+ private fun BinaryXmlPullParser.parseAppIdPermissions(state: AccessState, userId: Int) {
val userState = state.userStates[userId]
forEachTag {
when (tagName) {
@@ -139,7 +139,7 @@
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
}
}
- userState.uidPermissionFlags.retainAllIndexed { _, appId, _ ->
+ userState.appIdPermissionFlags.retainAllIndexed { _, appId, _ ->
val hasAppId = appId in state.systemState.appIds
if (!hasAppId) {
Log.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
@@ -151,34 +151,34 @@
private fun BinaryXmlPullParser.parseAppId(userState: UserState) {
val appId = getAttributeIntOrThrow(ATTR_ID)
val permissionFlags = IndexedMap<String, Int>()
- userState.uidPermissionFlags[appId] = permissionFlags
- parseAppIdPermissions(permissionFlags)
+ userState.appIdPermissionFlags[appId] = permissionFlags
+ parsePermissionStates(permissionFlags)
}
- private fun BinaryXmlPullParser.parseAppIdPermissions(
+ private fun BinaryXmlPullParser.parsePermissionStates(
permissionFlags: IndexedMap<String, Int>
) {
forEachTag {
when (tagName) {
- TAG_PERMISSION -> parseAppIdPermission(permissionFlags)
+ TAG_PERMISSION -> parsePermissionState(permissionFlags)
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
}
}
}
- private fun BinaryXmlPullParser.parseAppIdPermission(permissionFlags: IndexedMap<String, Int>) {
+ private fun BinaryXmlPullParser.parsePermissionState(permissionFlags: IndexedMap<String, Int>) {
val name = getAttributeValueOrThrow(ATTR_NAME).intern()
val flags = getAttributeIntOrThrow(ATTR_FLAGS)
permissionFlags[name] = flags
}
fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- serializePermissionFlags(state.userStates[userId])
+ serializeAppIdPermissions(state.userStates[userId])
}
- private fun BinaryXmlSerializer.serializePermissionFlags(userState: UserState) {
- tag(TAG_PERMISSIONS) {
- userState.uidPermissionFlags.forEachIndexed { _, appId, permissionFlags ->
+ private fun BinaryXmlSerializer.serializeAppIdPermissions(userState: UserState) {
+ tag(TAG_APP_ID_PERMISSIONS) {
+ userState.appIdPermissionFlags.forEachIndexed { _, appId, permissionFlags ->
serializeAppId(appId, permissionFlags)
}
}
@@ -190,19 +190,19 @@
) {
tag(TAG_APP_ID) {
attributeInt(ATTR_ID, appId)
- serializeAppIdPermissions(permissionFlags)
+ serializePermissionStates(permissionFlags)
}
}
- private fun BinaryXmlSerializer.serializeAppIdPermissions(
+ private fun BinaryXmlSerializer.serializePermissionStates(
permissionFlags: IndexedMap<String, Int>
) {
permissionFlags.forEachIndexed { _, name, flags ->
- serializeAppIdPermission(name, flags)
+ serializePermissionState(name, flags)
}
}
- private fun BinaryXmlSerializer.serializeAppIdPermission(name: String, flags: Int) {
+ private fun BinaryXmlSerializer.serializePermissionState(name: String, flags: Int) {
tag(TAG_PERMISSION) {
attributeInterned(ATTR_NAME, name)
attributeInt(ATTR_FLAGS, flags)
@@ -210,9 +210,10 @@
}
companion object {
- private val LOG_TAG = UidPermissionPersistence::class.java.simpleName
+ private val LOG_TAG = AppIdPermissionPersistence::class.java.simpleName
private const val TAG_APP_ID = "app-id"
+ private const val TAG_APP_ID_PERMISSIONS = "app-id-permissions"
private const val TAG_PERMISSION = "permission"
private const val TAG_PERMISSIONS = "permissions"
private const val TAG_PERMISSION_TREES = "permission-trees"
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
similarity index 97%
rename from services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
rename to services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 5a7b37a..6673dc6 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -45,8 +45,10 @@
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
-class UidPermissionPolicy : SchemePolicy() {
- private val persistence = UidPermissionPersistence()
+class AppIdPermissionPolicy : SchemePolicy() {
+ private val persistence = AppIdPermissionPersistence()
+
+ private val migration = AppIdPermissionMigration()
@Volatile
private var onPermissionFlagsChangedListeners =
@@ -124,7 +126,7 @@
override fun MutateStateScope.onAppIdRemoved(appId: Int) {
newState.userStates.forEachValueIndexed { _, userState ->
- userState.uidPermissionFlags -= appId
+ userState.appIdPermissionFlags -= appId
userState.requestWrite()
// Skip notifying the change listeners since the app ID no longer exists.
}
@@ -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
@@ -595,7 +596,7 @@
requestedPermissions += it.androidPackage!!.requestedPermissions
}
newState.userStates.forEachIndexed { _, userId, userState ->
- userState.uidPermissionFlags[appId]?.forEachReversedIndexed { _, permissionName, _ ->
+ userState.appIdPermissionFlags[appId]?.forEachReversedIndexed { _, permissionName, _ ->
if (permissionName !in requestedPermissions) {
setPermissionFlags(appId, userId, permissionName, 0)
}
@@ -607,7 +608,7 @@
// If the app is updated, and has scoped storage permissions, then it is possible that the
// app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
newState.userStates.forEachIndexed { _, userId, userState ->
- userState.uidPermissionFlags[appId]?.forEachReversedIndexed {
+ userState.appIdPermissionFlags[appId]?.forEachReversedIndexed {
_, permissionName, oldFlags ->
if (permissionName !in STORAGE_AND_MEDIA_PERMISSIONS || oldFlags == 0) {
return@forEachReversedIndexed
@@ -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
)
@@ -1313,7 +1316,7 @@
}
fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
- state.userStates[userId]?.uidPermissionFlags?.get(appId)
+ state.userStates[userId]?.appIdPermissionFlags?.get(appId)
fun GetStateScope.getPermissionFlags(
appId: Int,
@@ -1333,7 +1336,7 @@
userId: Int,
permissionName: String
): Int =
- state.userStates[userId]?.uidPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
+ state.userStates[userId]?.appIdPermissionFlags?.get(appId).getWithDefault(permissionName, 0)
fun MutateStateScope.setPermissionFlags(
appId: Int,
@@ -1351,8 +1354,8 @@
flagValues: Int
): Boolean {
val userState = newState.userStates[userId]
- val uidPermissionFlags = userState.uidPermissionFlags
- var permissionFlags = uidPermissionFlags[appId]
+ val appIdPermissionFlags = userState.appIdPermissionFlags
+ var permissionFlags = appIdPermissionFlags[appId]
val oldFlags = permissionFlags.getWithDefault(permissionName, 0)
val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
if (oldFlags == newFlags) {
@@ -1360,11 +1363,11 @@
}
if (permissionFlags == null) {
permissionFlags = IndexedMap()
- uidPermissionFlags[appId] = permissionFlags
+ appIdPermissionFlags[appId] = permissionFlags
}
permissionFlags.putWithDefault(permissionName, newFlags, 0)
if (permissionFlags.isEmpty()) {
- uidPermissionFlags -= appId
+ appIdPermissionFlags -= appId
}
userState.requestWrite()
onPermissionFlagsChangedListeners.forEachIndexed { _, it ->
@@ -1385,8 +1388,16 @@
}
}
+ 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
+ private val LOG_TAG = AppIdPermissionPolicy::class.java.simpleName
private const val PLATFORM_PACKAGE_NAME = "android"
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..4b20472 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
@@ -137,7 +138,7 @@
* For example, this flag may be set in
* [com.android.server.pm.permission.DefaultPermissionGrantPolicy].
*
- * @see PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
+ * @see PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
*/
const val PREGRANT = 1 shl 9
@@ -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..9b69362 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
@@ -65,7 +65,7 @@
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.UidUri
-import com.android.server.permission.access.appop.UidAppOpPolicy
+import com.android.server.permission.access.appop.AppIdAppOpPolicy
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.andInv
import com.android.server.permission.access.util.hasAnyBit
@@ -101,7 +101,7 @@
private val service: AccessCheckingService
) : PermissionManagerServiceInterface {
private val policy =
- service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy
+ service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
private val context = service.context
private lateinit var metricsLogger: MetricsLogger
@@ -930,7 +930,8 @@
permissionName: String,
isGranted: Boolean
) {
- val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as UidAppOpPolicy
+ val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
+ AppIdAppOpPolicy
val appOpName = AppOpsManager.permissionToOp(permissionName)
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
@@ -1600,11 +1601,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) {
@@ -2090,7 +2103,7 @@
* Callback invoked when interesting actions have been taken on a permission.
*/
private inner class OnPermissionFlagsChangedListener :
- UidPermissionPolicy.OnPermissionFlagsChangedListener() {
+ AppIdPermissionPolicy.OnPermissionFlagsChangedListener() {
private var isPermissionFlagsChanged = false
private val runtimePermissionChangedUids = IntSet()
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/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/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/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 = "success_on_return@fixture.com";
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/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/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index a0fb3de..f368a66 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -78,6 +78,7 @@
import android.service.dreams.DreamManagerInternal;
import android.sysprop.PowerProperties;
import android.test.mock.MockContentResolver;
+import android.util.IntArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -2322,6 +2323,31 @@
verify(mLowPowerStandbyControllerMock).setActiveDuringMaintenance(false);
}
+ @Test
+ public void testPowerGroupInitialization_multipleDisplayGroups() {
+ IntArray displayGroupIds = IntArray.wrap(new int[]{1, 2, 3});
+ when(mDisplayManagerInternalMock.getDisplayGroupIds()).thenReturn(displayGroupIds);
+
+ createService();
+ startSystem();
+
+ // Power group for DEFAULT_DISPLAY_GROUP is added by default.
+ assertThat(mService.getPowerGroupSize()).isEqualTo(4);
+ }
+
+ @Test
+ public void testPowerGroupInitialization_multipleDisplayGroupsWithDefaultGroup() {
+ IntArray displayGroupIds = IntArray.wrap(new int[]{Display.DEFAULT_DISPLAY_GROUP, 1, 2, 3});
+ when(mDisplayManagerInternalMock.getDisplayGroupIds()).thenReturn(displayGroupIds);
+
+ createService();
+ startSystem();
+
+ // Power group for DEFAULT_DISPLAY_GROUP is added once even if getDisplayGroupIds() return
+ // an array including DEFAULT_DESIPLAY_GROUP.
+ assertThat(mService.getPowerGroupSize()).isEqualTo(4);
+ }
+
private WakeLock acquireWakeLock(String tag, int flags) {
IBinder token = new Binder();
String packageName = "pkg.name";
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/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/EnforcePermission/Android.bp b/tests/EnforcePermission/Android.bp
new file mode 100644
index 0000000..719a898
--- /dev/null
+++ b/tests/EnforcePermission/Android.bp
@@ -0,0 +1,22 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "frameworks-enforce-permission-test-aidl",
+ srcs: ["aidl/**/*.aidl"],
+}
diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl
new file mode 100644
index 0000000..1eb773d
--- /dev/null
+++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.tests.enforcepermission;
+
+interface INested {
+ @EnforcePermission("ACCESS_NETWORK_STATE")
+ void ProtectedByAccessNetworkState();
+
+ @EnforcePermission("READ_SYNC_SETTINGS")
+ void ProtectedByReadSyncSettings();
+}
diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
new file mode 100644
index 0000000..18e3aec
--- /dev/null
+++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.tests.enforcepermission;
+
+interface IProtected {
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternet();
+
+ @EnforcePermission("VIBRATE")
+ void ProtectedByVibrate();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndVibrateImplicitly();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndAccessNetworkStateImplicitly();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndReadSyncSettingsImplicitly();
+}
diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp
new file mode 100644
index 0000000..a4ac1d7
--- /dev/null
+++ b/tests/EnforcePermission/service-app/Android.bp
@@ -0,0 +1,32 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "EnforcePermissionTestHelper",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/EnforcePermission/service-app/AndroidManifest.xml b/tests/EnforcePermission/service-app/AndroidManifest.xml
new file mode 100644
index 0000000..ddafe15
--- /dev/null
+++ b/tests/EnforcePermission/service-app/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.tests.enforcepermission.service">
+ <application>
+ <service
+ android:name=".TestService"
+ android:exported="true" />
+
+ <service
+ android:name=".NestedTestService"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java
new file mode 100644
index 0000000..7879a12
--- /dev/null
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.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 android.tests.enforcepermission.service;
+
+import android.annotation.EnforcePermission;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.tests.enforcepermission.INested;
+import android.util.Log;
+
+public class NestedTestService extends Service {
+ private static final String TAG = "EnforcePermission.NestedTestService";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind");
+ return mBinder;
+ }
+
+ private final INested.Stub mBinder = new INested.Stub() {
+ @Override
+ @EnforcePermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public void ProtectedByAccessNetworkState() {
+ ProtectedByAccessNetworkState_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_SYNC_SETTINGS)
+ public void ProtectedByReadSyncSettings() {
+ ProtectedByReadSyncSettings_enforcePermission();
+ }
+ };
+}
diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java
new file mode 100644
index 0000000..e9b897d
--- /dev/null
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java
@@ -0,0 +1,119 @@
+/**
+ * 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.tests.enforcepermission.service;
+
+import android.annotation.EnforcePermission;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tests.enforcepermission.INested;
+import android.tests.enforcepermission.IProtected;
+import android.util.Log;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class TestService extends Service {
+
+ private static final String TAG = "EnforcePermission.TestService";
+ private volatile ServiceConnection mNestedServiceConnection;
+
+ @Override
+ public void onCreate() {
+ mNestedServiceConnection = new ServiceConnection();
+ Intent intent = new Intent(this, NestedTestService.class);
+ boolean bound = bindService(intent, mNestedServiceConnection, Context.BIND_AUTO_CREATE);
+ if (!bound) {
+ Log.wtf(TAG, "bindService() on NestedTestService failed");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ unbindService(mNestedServiceConnection);
+ }
+
+ private static final class ServiceConnection implements android.content.ServiceConnection {
+ private volatile CompletableFuture<INested> mFuture = new CompletableFuture<>();
+
+ public INested get() {
+ try {
+ return mFuture.get(1, TimeUnit.SECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ throw new RuntimeException("Unable to reach NestedTestService: " + e.getMessage());
+ }
+ }
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mFuture.complete(INested.Stub.asInterface(service));
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mFuture = new CompletableFuture<>();
+ }
+ };
+
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private final IProtected.Stub mBinder = new IProtected.Stub() {
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternet() {
+ ProtectedByInternet_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.VIBRATE)
+ public void ProtectedByVibrate() {
+ ProtectedByVibrate_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndVibrateImplicitly() {
+ ProtectedByInternetAndVibrateImplicitly_enforcePermission();
+
+ ProtectedByVibrate();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndAccessNetworkStateImplicitly() throws RemoteException {
+ ProtectedByInternetAndAccessNetworkStateImplicitly_enforcePermission();
+
+ mNestedServiceConnection.get().ProtectedByAccessNetworkState();
+
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndReadSyncSettingsImplicitly() throws RemoteException {
+ ProtectedByInternetAndReadSyncSettingsImplicitly_enforcePermission();
+
+ mNestedServiceConnection.get().ProtectedByReadSyncSettings();
+ }
+ };
+}
diff --git a/tests/EnforcePermission/test-app/Android.bp b/tests/EnforcePermission/test-app/Android.bp
new file mode 100644
index 0000000..305ed8f
--- /dev/null
+++ b/tests/EnforcePermission/test-app/Android.bp
@@ -0,0 +1,38 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "EnforcePermissionTests",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ data: [
+ ":EnforcePermissionTestHelper",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["general-tests"],
+}
diff --git a/tests/EnforcePermission/test-app/AndroidManifest.xml b/tests/EnforcePermission/test-app/AndroidManifest.xml
new file mode 100644
index 0000000..4a0c6a8
--- /dev/null
+++ b/tests/EnforcePermission/test-app/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.tests.enforcepermission.tests">
+
+ <!-- Expected for the tests (not actually used) -->
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+
+ <queries>
+ <package android:name="android.tests.enforcepermission.service" />
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.tests.enforcepermission.tests"/>
+</manifest>
diff --git a/tests/EnforcePermission/test-app/AndroidTest.xml b/tests/EnforcePermission/test-app/AndroidTest.xml
new file mode 100644
index 0000000..120381a
--- /dev/null
+++ b/tests/EnforcePermission/test-app/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs EnforcePermission End-to-End Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="EnforcePermissionTestHelper.apk"/>
+ <option name="test-file-name" value="EnforcePermissionTests.apk"/>
+ <option name="cleanup-apks" value="true" />
+ </target_preparer>
+
+ <option name="test-tag" value="EnforcePermissionTests"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.tests.enforcepermission.tests"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
new file mode 100644
index 0000000..d2a4a03
--- /dev/null
+++ b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
@@ -0,0 +1,129 @@
+/**
+ * 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.tests.enforcepermission.tests;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tests.enforcepermission.IProtected;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class ServiceTest {
+
+ private static final String TAG = "EnforcePermission.Tests";
+ private static final String SERVICE_NAME = "android.tests.enforcepermission.service";
+ private static final int SERVICE_TIMEOUT_SEC = 5;
+
+ private Context mContext;
+ private volatile ServiceConnection mServiceConnection;
+
+ @Before
+ public void bindTestService() throws Exception {
+ Log.d(TAG, "bindTestService");
+ mContext = InstrumentationRegistry.getTargetContext();
+ mServiceConnection = new ServiceConnection();
+ Intent intent = new Intent();
+ intent.setClassName(SERVICE_NAME, SERVICE_NAME + ".TestService");
+ assertTrue(mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE));
+ }
+
+ @After
+ public void unbindTestService() throws Exception {
+ mContext.unbindService(mServiceConnection);
+ }
+
+ private static final class ServiceConnection implements android.content.ServiceConnection {
+ private volatile CompletableFuture<IProtected> mFuture = new CompletableFuture<>();
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mFuture.complete(IProtected.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mFuture = new CompletableFuture<>();
+ }
+
+ public IProtected get() {
+ try {
+ return mFuture.get(SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ throw new RuntimeException("Unable to reach TestService: " + e.toString());
+ }
+ }
+ }
+
+ @Test
+ public void testImmediatePermissionGranted_succeeds()
+ throws RemoteException {
+ mServiceConnection.get().ProtectedByInternet();
+ }
+
+ @Test
+ public void testImmediatePermissionNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByVibrate());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedButImplicitLocalNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByInternetAndVibrateImplicitly());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedButImplicitNestedNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get()
+ .ProtectedByInternetAndAccessNetworkStateImplicitly());
+ assertThat(ex.getMessage(), containsString("ACCESS_NETWORK_STATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedAndImplicitNestedGranted_succeeds()
+ throws RemoteException {
+ mServiceConnection.get().ProtectedByInternetAndReadSyncSettingsImplicitly();
+ }
+}
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/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
new file mode 100644
index 0000000..1ccac13
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import android.tools.common.datatypes.component.ComponentNameMatcher
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
+
+class LetterboxAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+
+ fun clickRestart(wmHelper: WindowManagerStateHelper) {
+ val restartButton = uiDevice.wait(Until.findObject(By.res(
+ SYSTEMUI_PACKAGE, "size_compat_restart_button")), FIND_TIMEOUT)
+ restartButton?.run { restartButton.click() } ?: error("Restart button not found")
+
+ // size compat mode restart confirmation dialog button
+ val restartDialogButton = uiDevice.wait(Until.findObject(By.res(
+ SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")), FIND_TIMEOUT)
+ restartDialogButton?.run { restartDialogButton.click() }
+ ?: error("Restart dialog button not found")
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 5361d73f..1ec9ec9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -88,6 +88,18 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".NonResizeablePortraitActivity"
+ android:theme="@style/CutoutNever"
+ android:resizeableActivity="false"
+ android:screenOrientation="portrait"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeablePortraitActivity"
+ android:label="NonResizeablePortraitActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name=".LaunchNewActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
android:theme="@style/CutoutShortEdges"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index b61bc0c..9c3226b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -67,6 +67,12 @@
FLICKER_APP_PACKAGE + ".NonResizeableActivity");
}
+ public static class NonResizeablePortraitActivity {
+ public static final String LABEL = "NonResizeablePortraitActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
+ }
+
public static class DialogThemedActivity {
public static final String LABEL = "DialogThemedActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
new file mode 100644
index 0000000..4b420dc
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class NonResizeablePortraitActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_non_resizeable);
+ }
+}
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..5421abd 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.