Merge "HA still appears in Media device list even though it has been unpaired."
diff --git a/Android.bp b/Android.bp
index 0ac41c0..f1f7a8e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -468,8 +468,6 @@
sdk_version: "module_current",
min_sdk_version: "30",
srcs: [
- ":modules-utils-preconditions-srcs",
- "core/java/android/os/HandlerExecutor.java",
"core/java/com/android/internal/util/AsyncChannel.java",
"core/java/com/android/internal/util/AsyncService.java",
"core/java/com/android/internal/util/Protocol.java",
diff --git a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
index f3d7390..8d2d044 100644
--- a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java
@@ -37,15 +37,15 @@
/**
* A complete line-for-line reimplementation of
- * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of
+ * {@link com.android.internal.os.LongArrayMultiStateCounter}, only in Java instead of
* native.
*/
private static class TestLongArrayMultiStateCounter {
private final int mStateCount;
private final int mArrayLength;
private int mCurrentState;
- private long mLastStateChangeTimestampMs;
- private long mLastUpdateTimestampMs;
+ private long mLastStateChangeTimestampMs = -1;
+ private long mLastUpdateTimestampMs = -1;
private static class State {
private long mTimeInStateSinceUpdate;
@@ -56,13 +56,9 @@
private final long[] mLastTimeInFreq;
private final long[] mDelta;
- TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
- long timestampMs) {
+ TestLongArrayMultiStateCounter(int stateCount, int arrayLength) {
mStateCount = stateCount;
mArrayLength = arrayLength;
- mCurrentState = initialState;
- mLastStateChangeTimestampMs = timestampMs;
- mLastUpdateTimestampMs = timestampMs;
mStates = new State[stateCount];
for (int i = 0; i < mStateCount; i++) {
mStates[i] = new State();
@@ -73,12 +69,14 @@
}
public void setState(int state, long timestampMs) {
- if (timestampMs >= mLastStateChangeTimestampMs) {
- mStates[mCurrentState].mTimeInStateSinceUpdate +=
- timestampMs - mLastStateChangeTimestampMs;
- } else {
- for (int i = 0; i < mStateCount; i++) {
- mStates[i].mTimeInStateSinceUpdate = 0;
+ if (mLastStateChangeTimestampMs > 0) {
+ if (timestampMs >= mLastStateChangeTimestampMs) {
+ mStates[mCurrentState].mTimeInStateSinceUpdate +=
+ timestampMs - mLastStateChangeTimestampMs;
+ } else {
+ for (int i = 0; i < mStateCount; i++) {
+ mStates[i].mTimeInStateSinceUpdate = 0;
+ }
}
}
mCurrentState = state;
@@ -88,21 +86,23 @@
public void updateValue(long[] timeInFreq, long timestampMs) {
setState(mCurrentState, timestampMs);
- if (timestampMs > mLastUpdateTimestampMs) {
- if (delta(mLastTimeInFreq, timeInFreq, mDelta)) {
- long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
- for (int i = 0; i < mStateCount; i++) {
- long timeInState = mStates[i].mTimeInStateSinceUpdate;
- if (timeInState > 0) {
- add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
- mStates[i].mTimeInStateSinceUpdate = 0;
+ if (mLastUpdateTimestampMs >= 0) {
+ if (timestampMs > mLastUpdateTimestampMs) {
+ if (delta(mLastTimeInFreq, timeInFreq, mDelta)) {
+ long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
+ for (int i = 0; i < mStateCount; i++) {
+ long timeInState = mStates[i].mTimeInStateSinceUpdate;
+ if (timeInState > 0) {
+ add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
+ mStates[i].mTimeInStateSinceUpdate = 0;
+ }
}
+ } else {
+ throw new RuntimeException();
}
- } else {
+ } else if (timestampMs < mLastUpdateTimestampMs) {
throw new RuntimeException();
}
- } else if (timestampMs < mLastUpdateTimestampMs) {
- throw new RuntimeException();
}
System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength);
mLastUpdateTimestampMs = timestampMs;
@@ -142,7 +142,7 @@
@Test
public void javaImplementation() {
TestLongArrayMultiStateCounter counter =
- new TestLongArrayMultiStateCounter(2, 4, 0, 1000);
+ new TestLongArrayMultiStateCounter(2, 4);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
long time = 1000;
long[] timeInFreq = {100, 200, 300, 400};
@@ -156,7 +156,7 @@
@Test
public void nativeImplementation() {
- LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
long time = 1000;
LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
diff --git a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
index 2700fff..e3691a7 100644
--- a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
+++ b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
@@ -64,9 +64,13 @@
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
os.reset();
- final FastDataOutput out = new FastDataOutput(os, BUFFER_SIZE);
- doWrite(out);
- out.flush();
+ final FastDataOutput out = FastDataOutput.obtain(os);
+ try {
+ doWrite(out);
+ out.flush();
+ } finally {
+ out.release();
+ }
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index d4e3239..b49bbc5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -18,6 +18,7 @@
import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
import static android.os.Process.INVALID_UID;
+import android.Manifest;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
@@ -331,6 +332,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -343,7 +345,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
@@ -422,6 +424,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -430,7 +433,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -457,6 +460,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -465,7 +469,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -495,6 +499,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -507,7 +512,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
@@ -584,6 +589,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -596,7 +602,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
@@ -668,6 +674,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -680,7 +687,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -737,6 +744,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -749,7 +757,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -805,6 +813,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -813,7 +822,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -837,6 +846,7 @@
Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -845,7 +855,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -874,6 +884,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -882,7 +893,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -929,6 +940,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -937,7 +949,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -994,6 +1006,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -1002,7 +1015,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -1044,6 +1057,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1056,7 +1070,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
@@ -1133,6 +1147,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1145,7 +1160,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -1200,6 +1215,7 @@
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -1208,7 +1224,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -1233,6 +1249,7 @@
Objects.requireNonNull(userHandle);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1245,7 +1262,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -1288,6 +1305,7 @@
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@@ -1301,7 +1319,7 @@
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
Context targetUserContext = mContext.createContextAsUser(targetUser,
@@ -1389,12 +1407,22 @@
/**
* Helper for dealing with incoming user arguments to system service calls.
*
+ * <p>Takes care of checking permissions and if the target is special user, this method will
+ * simply throw.
+ *
* @param targetUserHandle The user which the caller is requesting to execute as.
+ * @param callingPid The actual pid of the caller as determined by Binder.
* @param callingUid The actual uid of the caller as determined by Binder.
+ *
* @return the user handle that the call should run as. Will always be a concrete user.
+ *
+ * @throws IllegalArgumentException if the target user is a special user.
+ * @throws SecurityException if caller trying to interact across user without
+ * {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL}
*/
@NonNull
- private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingUid) {
+ private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingPid,
+ int callingUid) {
UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
if (callingUserHandle.equals(targetUserHandle)) {
return targetUserHandle;
@@ -1406,9 +1434,16 @@
"Call does not support special user " + targetUserHandle);
}
+ if (mContext.checkPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid,
+ callingUid) == PackageManager.PERMISSION_GRANTED) {
+ return targetUserHandle;
+ }
throw new SecurityException(
- "Requested user, " + targetUserHandle + ", is not the same as the calling user, "
- + callingUserHandle + ".");
+ "Permission denied while calling from uid " + callingUid
+ + " with " + targetUserHandle + "; Requires permission: "
+ + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index d0a155d..d21a0ea 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -621,10 +621,6 @@
mBackgroundRestrictedUidPackages = Collections.emptySet();
return;
}
- if (mForceAllAppsStandby) {
- mBackgroundRestrictedUidPackages = null;
- return;
- }
Set<Pair<Integer, String>> fasUidPkgs = new ArraySet<>();
for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) {
fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i));
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index 2588688..bc0bb47 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,8 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#include <sys/types.h>
+
#include <random>
#include <string>
diff --git a/core/api/current.txt b/core/api/current.txt
index 78c4cdf..fab08a5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9041,6 +9041,15 @@
field public static final int TELEPHONY = 4194304; // 0x400000
}
+ public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method public void close();
+ method protected void finalize();
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
@@ -20505,7 +20514,7 @@
method public String getProperty(String);
method public int getRingerMode();
method @Deprecated public int getRouting(int);
- method @Nullable public android.media.Spatializer getSpatializer();
+ method @NonNull public android.media.Spatializer getSpatializer();
method public int getStreamMaxVolume(int);
method public int getStreamMinVolume(int);
method public int getStreamVolume(int);
@@ -23921,9 +23930,13 @@
public class Spatializer {
method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
+ method public int getImmersiveAudioLevel();
method public boolean isAvailable();
method public boolean isEnabled();
method public void removeOnSpatializerStateChangedListener(@NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; // 0x1
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; // 0x0
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1; // 0xffffffff
}
public static interface Spatializer.OnSpatializerStateChangedListener {
@@ -53425,7 +53438,6 @@
method public void onPermissionRequest(android.webkit.PermissionRequest);
method public void onPermissionRequestCanceled(android.webkit.PermissionRequest);
method public void onProgressChanged(android.webkit.WebView, int);
- method @Deprecated public void onReachedMaxAppCacheSize(long, long, android.webkit.WebStorage.QuotaUpdater);
method public void onReceivedIcon(android.webkit.WebView, android.graphics.Bitmap);
method public void onReceivedTitle(android.webkit.WebView, String);
method public void onReceivedTouchIconUrl(android.webkit.WebView, String, boolean);
@@ -53577,9 +53589,6 @@
method public abstract void setAllowFileAccess(boolean);
method @Deprecated public abstract void setAllowFileAccessFromFileURLs(boolean);
method @Deprecated public abstract void setAllowUniversalAccessFromFileURLs(boolean);
- method @Deprecated public abstract void setAppCacheEnabled(boolean);
- method @Deprecated public abstract void setAppCacheMaxSize(long);
- method @Deprecated public abstract void setAppCachePath(String);
method public abstract void setBlockNetworkImage(boolean);
method public abstract void setBlockNetworkLoads(boolean);
method public abstract void setBuiltInZoomControls(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b47c9cf..3ceb8873 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -35,8 +35,14 @@
}
public final class PendingIntent implements android.os.Parcelable {
+ method public boolean addCancelListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.PendingIntent.CancelListener);
method @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public boolean intentFilterEquals(@Nullable android.app.PendingIntent);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_INTENT_SENDER_INTENT) public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+ method public void removeCancelListener(@NonNull android.app.PendingIntent.CancelListener);
+ }
+
+ public static interface PendingIntent.CancelListener {
+ method public void onCancelled(@NonNull android.app.PendingIntent);
}
public class StatusBarManager {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 235de26..07639fb 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -568,6 +568,16 @@
package android.webkit {
+ public class WebChromeClient {
+ method @Deprecated public void onReachedMaxAppCacheSize(long, long, android.webkit.WebStorage.QuotaUpdater);
+ }
+
+ public abstract class WebSettings {
+ method @Deprecated public void setAppCacheEnabled(boolean);
+ method @Deprecated public void setAppCacheMaxSize(long);
+ method @Deprecated public void setAppCachePath(String);
+ }
+
public class WebViewClient {
method public void onUnhandledInputEvent(android.webkit.WebView, android.view.InputEvent);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 27d78e5..171138e 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1967,9 +1967,25 @@
method public void onOobData(int, @NonNull android.bluetooth.OobData);
}
+ public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<java.lang.Integer> getAllGroupIds(@Nullable android.os.ParcelUuid);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.Map getGroupUuidMapByDevice(@Nullable android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.UUID groupLock(int, @Nullable java.util.concurrent.Executor, @Nullable android.bluetooth.BluetoothCsipSetCoordinator.ClientLockCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean groupUnlock(@NonNull java.util.UUID);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_DEVICE_AVAILABLE = "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
+ }
+
+ public static interface BluetoothCsipSetCoordinator.ClientLockCallback {
+ method public void onGroupLockSet(int, int, boolean);
+ }
+
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean canBondWithoutDialog();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean fetchUuidsWithSdp(int);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public byte[] getMetadata(int);
@@ -2021,7 +2037,10 @@
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startScoUsingVirtualVoiceCall();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall();
}
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -3301,6 +3320,7 @@
package android.hardware.hdmi {
public abstract class HdmiClient {
+ method public void deviceSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
method public android.hardware.hdmi.HdmiDeviceInfo getActiveSource();
method public void sendKeyEvent(int, boolean);
method public void sendVendorCommand(int, byte[], boolean);
@@ -3644,7 +3664,7 @@
public final class HdmiTvClient extends android.hardware.hdmi.HdmiClient {
method public void clearTimerRecording(int, int, android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource);
- method public void deviceSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
+ method @Deprecated public void deviceSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
method @Deprecated public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getDeviceList();
method public int getDeviceType();
method public void portSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
@@ -8581,6 +8601,7 @@
public class SystemConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
}
@@ -8956,6 +8977,8 @@
public final class PermissionManager {
method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
+ method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
+ method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionGrantedPackages();
method @NonNull @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public java.util.Set<java.lang.String> getAutoRevokeExemptionRequestedPackages();
method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
@@ -8963,6 +8986,9 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
+ field public static final int PERMISSION_GRANTED = 0; // 0x0
+ field public static final int PERMISSION_HARD_DENIED = 2; // 0x2
+ field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
}
public static final class PermissionManager.SplitPermissionInfo {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index efd96b9..96fa9c4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -313,11 +313,17 @@
}
public final class PendingIntent implements android.os.Parcelable {
+ method public boolean addCancelListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.PendingIntent.CancelListener);
method @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public boolean intentFilterEquals(@Nullable android.app.PendingIntent);
method @NonNull @RequiresPermission("android.permission.GET_INTENT_SENDER_INTENT") public java.util.List<android.content.pm.ResolveInfo> queryIntentComponents(int);
+ method public void removeCancelListener(@NonNull android.app.PendingIntent.CancelListener);
field @Deprecated public static final int FLAG_MUTABLE_UNAUDITED = 33554432; // 0x2000000
}
+ public static interface PendingIntent.CancelListener {
+ method public void onCancelled(@NonNull android.app.PendingIntent);
+ }
+
public final class PictureInPictureParams implements android.os.Parcelable {
method public java.util.List<android.app.RemoteAction> getActions();
method public float getAspectRatio();
@@ -3324,6 +3330,7 @@
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);
@@ -3339,6 +3346,15 @@
field @NonNull public static final android.os.Parcelable.Creator<android.window.WindowContainerTransaction> CREATOR;
}
+ public static class WindowContainerTransaction.TaskFragmentAdjacentParams {
+ ctor public WindowContainerTransaction.TaskFragmentAdjacentParams();
+ ctor public WindowContainerTransaction.TaskFragmentAdjacentParams(@NonNull android.os.Bundle);
+ method public void setShouldDelayPrimaryLastActivityRemoval(boolean);
+ method public void setShouldDelaySecondaryLastActivityRemoval(boolean);
+ method public boolean shouldDelayPrimaryLastActivityRemoval();
+ method public boolean shouldDelaySecondaryLastActivityRemoval();
+ }
+
public abstract class WindowContainerTransactionCallback {
ctor public WindowContainerTransactionCallback();
method public abstract void onTransactionReady(int, @NonNull android.view.SurfaceControl.Transaction);
diff --git a/core/java/android/annotation/SuppressLint.java b/core/java/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..2d3456b
--- /dev/null
+++ b/core/java/android/annotation/SuppressLint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should ignore the specified warnings for the annotated element. */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+ /**
+ * The set of warnings (identified by the lint issue id) that should be
+ * ignored by lint. It is not an error to specify an unrecognized name.
+ */
+ String[] value();
+}
diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
new file mode 100644
index 0000000..975318e
--- /dev/null
+++ b/core/java/android/annotation/TargetApi.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should treat this type as targeting a given API level, no matter what the
+ project target is. */
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface TargetApi {
+ /**
+ * This sets the target api level for the type..
+ */
+ int value();
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4f03b4f..c17651e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1906,10 +1906,14 @@
}
/**
- * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
- * {@link #onPause}, for your activity to start interacting with the user. This is an indicator
- * that the activity became active and ready to receive input. It is on top of an activity stack
- * and visible to user.
+ * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or {@link #onPause}. This
+ * is usually a hint for your activity to start interacting with the user, which is a good
+ * indicator that the activity became active and ready to receive input. This sometimes could
+ * also be a transit state toward another resting state. For instance, an activity may be
+ * relaunched to {@link #onPause} due to configuration changes and the activity was visible,
+ * but wasn’t the top-most activity of an activity task. {@link #onResume} is guaranteed to be
+ * called before {@link #onPause} in this case which honors the activity lifecycle policy and
+ * the activity eventually rests in {@link #onPause}.
*
* <p>On platform versions prior to {@link android.os.Build.VERSION_CODES#Q} this is also a good
* place to try to open exclusive-access devices or to get access to singleton resources.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2e22b92..34023a4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6343,7 +6343,13 @@
final File cacheDir = context.getCacheDir();
if (cacheDir != null) {
// Provide a usable directory for temporary files
- System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+ String tmpdir = cacheDir.getAbsolutePath();
+ System.setProperty("java.io.tmpdir", tmpdir);
+ try {
+ android.system.Os.setenv("TMPDIR", tmpdir, true);
+ } catch (ErrnoException ex) {
+ Log.w(TAG, "Unable to initialize $TMPDIR", ex);
+ }
} else {
Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property "
+ "due to missing cache directory");
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index 1f323c3..ae3a9e6 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -74,6 +74,11 @@
return false;
}
+ private static int unsupportedInt() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning -1", new Exception());
+ return -1;
+ }
+
@Override
public Drawable getDrawable() {
return unsupported();
@@ -189,12 +194,12 @@
@Override
public int getWallpaperId(int which) {
- return unsupported();
+ return unsupportedInt();
}
@Override
public int getWallpaperIdForUser(int which, int userId) {
- return unsupported();
+ return unsupportedInt();
}
@Override
@@ -209,7 +214,8 @@
@Override
public int setResource(int resid, int which) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -220,19 +226,22 @@
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which,
int userId) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -243,13 +252,15 @@
@Override
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup,
int which) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -259,12 +270,12 @@
@Override
public int getDesiredMinimumWidth() {
- return unsupported();
+ return unsupportedInt();
}
@Override
public int getDesiredMinimumHeight() {
- return unsupported();
+ return unsupportedInt();
}
@Override
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 6b738ff..47e4810 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -250,7 +250,14 @@
in String[] resolvedTypes, int flags, in Bundle options, int userId);
void cancelIntentSender(in IIntentSender sender);
ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender);
- void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
+ /**
+ This method used to be called registerIntentSenderCancelListener(), was void, and
+ would call `receiver` if the PI has already been canceled.
+ Now it returns false if the PI is cancelled, without calling `receiver`.
+ The method was renamed to catch calls to the original method.
+ */
+ boolean registerIntentSenderCancelListenerEx(in IIntentSender sender,
+ in IResultReceiver receiver);
void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
void enterSafeMode();
void noteWakeupAlarm(in IIntentSender sender, in WorkSource workSource, int sourceUid,
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 0136a35..0863500 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -53,8 +53,10 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Pair;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import java.lang.annotation.Retention;
@@ -62,6 +64,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* A description of an Intent and target action to perform with it. Instances
@@ -127,7 +130,27 @@
private final IIntentSender mTarget;
private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken;
- private ArraySet<CancelListener> mCancelListeners;
+
+ /**
+ * To protect {@link #mCancelListeners}. We could stop lazy-initialization and synchronize
+ * on {@link #mCancelListeners} directly, and that wouldn't increase allocations
+ * (an empty ArraySet won't causew extra allocations), but
+ * because an empty ArraySet is slightly larger than an Object, and because
+ * {@link #addCancelListener} is rarely used, having a separate lock object would probably
+ * be a net win.
+ */
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ArraySet<Pair<Executor, CancelListener>> mCancelListeners;
+
+ /**
+ * Whether the PI is canceld or not. Note this is essentially a "cache" that's updated
+ * only when the client uses {@link #addCancelListener}. Even if this is fase, that
+ * still doesn't know the PI is *not* cancled, but if it's true, this PI is definitely canceled.
+ */
+ @GuardedBy("mLock")
+ private boolean mCanceled;
// cached pending intent information
private @Nullable PendingIntentInfo mCachedInfo;
@@ -1048,19 +1071,38 @@
}
/**
- * Register a listener to when this pendingIntent is cancelled. There are no guarantees on which
- * thread a listener will be called and it's up to the caller to synchronize. This may
- * trigger a synchronous binder call so should therefore usually be called on a background
- * thread.
+ * @hide
+ * @deprecated use {@link #addCancelListener(Executor, CancelListener)} instead.
+ */
+ @Deprecated
+ public void registerCancelListener(@NonNull CancelListener cancelListener) {
+ if (!addCancelListener(Runnable::run, cancelListener)) {
+ // Call the callback right away synchronously, if the PI has been canceled already.
+ cancelListener.onCancelled(this);
+ }
+ }
+
+ /**
+ * Register a listener to when this pendingIntent is cancelled.
+ *
+ * @return true if the listener has been set successfully. false if the {@link PendingIntent}
+ * has already been canceled.
*
* @hide
*/
- public void registerCancelListener(CancelListener cancelListener) {
- synchronized (this) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public boolean addCancelListener(@NonNull Executor executor,
+ @NonNull CancelListener cancelListener) {
+ synchronized (mLock) {
+ if (mCanceled) {
+ return false;
+ }
+
if (mCancelReceiver == null) {
mCancelReceiver = new IResultReceiver.Stub() {
@Override
- public void send(int resultCode, Bundle resultData) throws RemoteException {
+ public void send(int resultCode, Bundle resultData) {
notifyCancelListeners();
}
};
@@ -1069,42 +1111,69 @@
mCancelListeners = new ArraySet<>();
}
boolean wasEmpty = mCancelListeners.isEmpty();
- mCancelListeners.add(cancelListener);
+ mCancelListeners.add(Pair.create(executor, cancelListener));
if (wasEmpty) {
+ boolean success;
try {
- ActivityManager.getService().registerIntentSenderCancelListener(mTarget,
- mCancelReceiver);
+ success = ActivityManager.getService().registerIntentSenderCancelListenerEx(
+ mTarget, mCancelReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ if (!success) {
+ mCanceled = true;
+ }
+ return success;
+ } else {
+ return !mCanceled;
}
}
}
private void notifyCancelListeners() {
- ArraySet<CancelListener> cancelListeners;
- synchronized (this) {
+ ArraySet<Pair<Executor, CancelListener>> cancelListeners;
+ synchronized (mLock) {
+ if (mCancelListeners == null || mCancelListeners.size() == 0) {
+ return;
+ }
+ mCanceled = true;
cancelListeners = new ArraySet<>(mCancelListeners);
+ mCancelListeners.clear();
}
int size = cancelListeners.size();
for (int i = 0; i < size; i++) {
- cancelListeners.valueAt(i).onCancelled(this);
+ final Pair<Executor, CancelListener> pair = cancelListeners.valueAt(i);
+ pair.first.execute(() -> pair.second.onCancelled(this));
}
}
/**
+ * @hide
+ * @deprecated use {@link #removeCancelListener(CancelListener)} instead.
+ */
+ @Deprecated
+ public void unregisterCancelListener(CancelListener cancelListener) {
+ removeCancelListener(cancelListener);
+ }
+
+ /**
* Un-register a listener to when this pendingIntent is cancelled.
*
* @hide
*/
- public void unregisterCancelListener(CancelListener cancelListener) {
- synchronized (this) {
- if (mCancelListeners == null) {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public void removeCancelListener(@NonNull CancelListener cancelListener) {
+ synchronized (mLock) {
+ if (mCancelListeners.size() == 0) {
return;
}
- boolean wasEmpty = mCancelListeners.isEmpty();
- mCancelListeners.remove(cancelListener);
- if (mCancelListeners.isEmpty() && !wasEmpty) {
+ for (int i = mCancelListeners.size() - 1; i >= 0; i--) {
+ if (mCancelListeners.valueAt(i).second == cancelListener) {
+ mCancelListeners.removeAt(i);
+ }
+ }
+ if (mCancelListeners.isEmpty()) {
try {
ActivityManager.getService().unregisterIntentSenderCancelListener(mTarget,
mCancelReceiver);
@@ -1401,13 +1470,15 @@
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public interface CancelListener {
/**
* Called when a Pending Intent is cancelled.
*
* @param intent The intent that was cancelled.
*/
- void onCancelled(PendingIntent intent);
+ void onCancelled(@NonNull PendingIntent intent);
}
private PendingIntentInfo getCachedInfo() {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index d9a33902..ee50634 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -52,7 +52,7 @@
*/
@SystemService(Context.STATUS_BAR_SERVICE)
public class StatusBarManager {
-
+ // LINT.IfChange
/** @hide */
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
/** @hide */
@@ -150,6 +150,7 @@
})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
+ // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt)
/**
* Default disable flags for setup
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 963d2a8..a53ef1b 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -563,7 +563,7 @@
}
if (returnDefault) {
Bitmap defaultWallpaper = mDefaultWallpaper;
- if (defaultWallpaper == null) {
+ if (defaultWallpaper == null || defaultWallpaper.isRecycled()) {
defaultWallpaper = getDefaultWallpaper(context, which);
synchronized (this) {
mDefaultWallpaper = defaultWallpaper;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7a043a8..f572576 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9743,6 +9743,27 @@
}
/**
+ * @param userId The user for whom to retrieve information.
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ * @return Details of admin and user which enforced the restriction for the userId. If
+ * restriction is null, profile owner for the user or device owner info is returned.
+ * @hide
+ */
+ public @Nullable Bundle getEnforcingAdminAndUserDetails(int userId,
+ @Nullable String restriction) {
+ if (mService != null) {
+ try {
+ return mService.getEnforcingAdminAndUserDetails(userId, restriction);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fc49f79..cb196a6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -251,6 +251,7 @@
boolean isNotificationListenerServicePermitted(in String packageName, int userId);
Intent createAdminSupportIntent(in String restriction);
+ Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 063ba11..2e94dd1 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -143,7 +143,7 @@
public ComponentName provider;
/**
- * The default height of the widget when added to a host, in dp. The widget will get
+ * The default height of the widget when added to a host, in px. The widget will get
* at least this width, and will often be given more, depending on the host.
*
* <p>This field corresponds to the <code>android:minWidth</code> attribute in
@@ -152,7 +152,7 @@
public int minWidth;
/**
- * The default height of the widget when added to a host, in dp. The widget will get
+ * The default height of the widget when added to a host, in px. The widget will get
* at least this height, and will often be given more, depending on the host.
*
* <p>This field corresponds to the <code>android:minHeight</code> attribute in
@@ -161,7 +161,7 @@
public int minHeight;
/**
- * Minimum width (in dp) which the widget can be resized to. This field has no effect if it
+ * Minimum width (in px) which the widget can be resized to. This field has no effect if it
* is greater than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:minResizeWidth</code> attribute in
@@ -170,7 +170,7 @@
public int minResizeWidth;
/**
- * Minimum height (in dp) which the widget can be resized to. This field has no effect if it
+ * Minimum height (in px) which the widget can be resized to. This field has no effect if it
* is greater than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:minResizeHeight</code> attribute in
@@ -179,7 +179,7 @@
public int minResizeHeight;
/**
- * Maximum width (in dp) which the widget can be resized to. This field has no effect if it is
+ * Maximum width (in px) which the widget can be resized to. This field has no effect if it is
* smaller than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:maxResizeWidth</code> attribute in the
@@ -189,7 +189,7 @@
public int maxResizeWidth;
/**
- * Maximum height (in dp) which the widget can be resized to. This field has no effect if it is
+ * Maximum height (in px) which the widget can be resized to. This field has no effect if it is
* smaller than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:maxResizeHeight</code> attribute in the
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2b28c11..77abbe9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3068,6 +3068,10 @@
} else if (profile == BluetoothProfile.VOLUME_CONTROL) {
BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) {
+ BluetoothCsipSetCoordinator csipSetCoordinator =
+ new BluetoothCsipSetCoordinator(context, listener, this);
+ return true;
} else {
return false;
}
@@ -3165,6 +3169,11 @@
BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
vcs.close();
break;
+ case BluetoothProfile.CSIP_SET_COORDINATOR:
+ BluetoothCsipSetCoordinator csipSetCoordinator =
+ (BluetoothCsipSetCoordinator) proxy;
+ csipSetCoordinator.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
new file mode 100644
index 0000000..f0a8df0
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the public APIs to control the Bluetooth CSIP set coordinator.
+ *
+ * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothCsipSetCoordinator proxy object.
+ *
+ */
+public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable {
+ private static final String TAG = "BluetoothCsipSetCoordinator";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ private CloseGuard mCloseGuard;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public interface ClientLockCallback {
+ /**
+ * @hide
+ */
+ @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked);
+ }
+
+ private static class BluetoothCsipSetCoordinatorLockCallbackDelegate
+ extends IBluetoothCsipSetCoordinatorLockCallback.Stub {
+ private final ClientLockCallback mCallback;
+ private final Executor mExecutor;
+
+ BluetoothCsipSetCoordinatorLockCallbackDelegate(
+ Executor executor, ClientLockCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
+ mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked));
+ }
+ };
+
+ /**
+ * Intent used to broadcast the change in connection state of the CSIS
+ * Client.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to expose broadcast receiving device.
+ *
+ * <p>This intent will have 2 extras:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_DEVICE_AVAILABLE =
+ "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
+
+ /**
+ * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ * Contains the group id.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID";
+
+ /**
+ * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE";
+
+ /**
+ * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_TYPE_UUID =
+ "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID";
+
+ /**
+ * Intent used to broadcast information about identified set member
+ * ready to connect.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE =
+ "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
+
+ /**
+ * This represents an invalid group ID.
+ *
+ * @hide
+ */
+ public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID;
+
+ /**
+ * Indicating that group was locked with success.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_SUCCESS = 0;
+
+ /**
+ * Indicating that group locked failed due to invalid group ID.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1;
+
+ /**
+ * Indicating that group locked failed due to empty group.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
+
+ /**
+ * Indicating that group locked failed due to group members being disconnected.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
+
+ /**
+ * Indicating that group locked failed due to group member being already locked.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
+
+ /**
+ * Indicating that group locked failed due to other reason.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5;
+
+ /**
+ * Indicating that group member in locked state was lost.
+ *
+ * @hide
+ */
+ public static final int LOCKED_GROUP_MEMBER_LOST = 6;
+
+ private final BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+ private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG,
+ IBluetoothCsipSetCoordinator.class.getName()) {
+ @Override
+ public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
+ return IBluetoothCsipSetCoordinator.Stub.asInterface(
+ Binder.allowBlocking(service));
+ }
+ };
+
+ /**
+ * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local
+ * Bluetooth CSIS service.
+ */
+ /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener, BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+ mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * @hide
+ */
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ /**
+ * @hide
+ */
+ public void close() {
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothCsipSetCoordinator getService() {
+ return mProfileConnector.getService();
+ }
+
+ /**
+ * Lock the set.
+ * @param groupId group ID to lock,
+ * @param executor callback executor,
+ * @param cb callback to report lock and unlock events - stays valid until the app unlocks
+ * using the returned lock identifier or the lock timeouts on the remote side,
+ * as per CSIS specification,
+ * @return unique lock identifier used for unlocking or null if lock has failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public
+ @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
+ @Nullable ClientLockCallback cb) {
+ if (VDBG) {
+ log("groupLockSet()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+ if ((executor != null) && (cb != null)) {
+ delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
+ }
+ return service.groupLock(groupId, delegate, mAttributionSource).getUuid();
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return null;
+ }
+ }
+
+ /**
+ * Unlock the set.
+ * @param lockUuid unique lock identifier
+ * @return true if unlocked, false on error
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean groupUnlock(@NonNull UUID lockUuid) {
+ if (VDBG) {
+ log("groupLockSet()");
+ }
+ if (lockUuid == null) {
+ return false;
+ }
+
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource);
+ return true;
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
+ * Get device's groups.
+ * @param device the active device
+ * @return Map of groups ids and related UUIDs
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getGroupUuidMapByDevice()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ return service.getGroupUuidMapByDevice(device, mAttributionSource);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new HashMap<>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new HashMap<>();
+ }
+ }
+
+ /**
+ * Get group id for the given UUID
+ * @param uuid
+ * @return list of group IDs
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
+ if (VDBG) {
+ log("getAllGroupIds()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ return service.getAllGroupIds(uuid, mAttributionSource);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<Integer>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<Integer>();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ if (VDBG) {
+ log("getConnectedDevices()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return service.getConnectedDevices(mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public
+ @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ if (VDBG) {
+ log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return service.getDevicesMatchingConnectionStates(states, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public
+ @BluetoothProfile.BtProfileState int getConnectionState(
+ @Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getState(" + device + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionState(device, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setConnectionPolicy(
+ @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
+ if (DBG) {
+ log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getConnectionPolicy(" + device + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.getConnectionPolicy(device, mAttributionSource);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ }
+
+ private boolean isEnabled() {
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+ }
+
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index eee981d..97b4a53 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1448,7 +1448,7 @@
* @throws IllegalArgumentException if an invalid transport was specified
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
@RequiresLegacyBluetoothAdminPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index b594ae3..c046324 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,17 +16,16 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Attributable;
import android.content.AttributionSource;
@@ -1105,13 +1104,13 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
+ @SystemApi
@RequiresLegacyBluetoothAdminPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.MODIFY_PHONE_STATE,
})
- @UnsupportedAppUsage
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
@@ -1140,13 +1139,13 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
+ @SystemApi
@RequiresLegacyBluetoothAdminPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.MODIFY_PHONE_STATE,
})
- @UnsupportedAppUsage
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
@@ -1343,9 +1342,10 @@
* @return true if in-band ringing is enabled, false if in-band ringing is disabled
* @hide
*/
+ @SystemApi
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isInbandRingingEnabled() {
if (DBG) {
log("isInbandRingingEnabled()");
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
index a630873..37bd6d1 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
@@ -16,7 +16,6 @@
package android.companion;
-import android.companion.Association;
import android.companion.AssociationRequest;
import android.companion.IFindDeviceCallback;
import com.android.internal.infra.AndroidFuture;
@@ -28,5 +27,5 @@
in AssociationRequest request,
in String callingPackage,
in IFindDeviceCallback findCallback,
- in AndroidFuture<Association> serviceCallback);
+ in AndroidFuture<String> serviceCallback);
}
diff --git a/core/java/android/content/pm/InstantAppIntentFilter.java b/core/java/android/content/pm/InstantAppIntentFilter.java
index 7c63406..123d2ba 100644
--- a/core/java/android/content/pm/InstantAppIntentFilter.java
+++ b/core/java/android/content/pm/InstantAppIntentFilter.java
@@ -46,7 +46,7 @@
InstantAppIntentFilter(Parcel in) {
mSplitName = in.readString();
- in.readList(mFilters, null /*loader*/);
+ in.readList(mFilters, getClass().getClassLoader());
}
public String getSplitName() {
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 4c963a6..9881564 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -143,7 +143,7 @@
mDigest = in.readParcelable(null /*loader*/);
mPackageName = in.readString();
mFilters = new ArrayList<>();
- in.readList(mFilters, null /*loader*/);
+ in.readTypedList(mFilters, InstantAppIntentFilter.CREATOR);
mVersionCode = in.readLong();
}
}
@@ -204,7 +204,7 @@
}
out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
- out.writeList(mFilters);
+ out.writeTypedList(mFilters);
out.writeLong(mVersionCode);
}
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
new file mode 100644
index 0000000..603b06d
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Common constants for biometric overlays.
+ * @hide
+ */
+public interface BiometricOverlayConstants {
+ /** Unknown usage. */
+ int REASON_UNKNOWN = 0;
+ /** User is about to enroll. */
+ int REASON_ENROLL_FIND_SENSOR = 1;
+ /** User is enrolling. */
+ int REASON_ENROLL_ENROLLING = 2;
+ /** Usage from BiometricPrompt. */
+ int REASON_AUTH_BP = 3;
+ /** Usage from Keyguard. */
+ int REASON_AUTH_KEYGUARD = 4;
+ /** Non-specific usage (from FingerprintManager). */
+ int REASON_AUTH_OTHER = 5;
+
+ @IntDef({REASON_UNKNOWN,
+ REASON_ENROLL_FIND_SENSOR,
+ REASON_ENROLL_ENROLLING,
+ REASON_AUTH_BP,
+ REASON_AUTH_KEYGUARD,
+ REASON_AUTH_OTHER})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ShowReason {}
+}
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.aidl b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
new file mode 100644
index 0000000..0981904
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+// @hide
+parcelable SensorLocationInternal;
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.java b/core/java/android/hardware/biometrics/SensorLocationInternal.java
new file mode 100644
index 0000000..fb25a2f
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The location of a sensor relative to a physical display.
+ *
+ * Note that the location may change depending on other attributes of the device, such as
+ * fold status, which are not yet included in this class.
+ * @hide
+ */
+public class SensorLocationInternal implements Parcelable {
+
+ /** Default value to use when the sensor's location is unknown or undefined. */
+ public static final SensorLocationInternal DEFAULT = new SensorLocationInternal("", 0, 0, 0);
+
+ /**
+ * The stable display id.
+ */
+ @NonNull
+ public final String displayId;
+
+ /**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the left edge of the screen.
+ */
+ public final int sensorLocationX;
+
+ /**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the top edge of the screen.
+ *
+ */
+ public final int sensorLocationY;
+
+ /**
+ * The radius of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
+ * of the sensor, in pixels.
+ */
+ public final int sensorRadius;
+
+ public SensorLocationInternal(@Nullable String displayId,
+ int sensorLocationX, int sensorLocationY, int sensorRadius) {
+ this.displayId = displayId != null ? displayId : "";
+ this.sensorLocationX = sensorLocationX;
+ this.sensorLocationY = sensorLocationY;
+ this.sensorRadius = sensorRadius;
+ }
+
+ protected SensorLocationInternal(Parcel in) {
+ displayId = in.readString16NoHelper();
+ sensorLocationX = in.readInt();
+ sensorLocationY = in.readInt();
+ sensorRadius = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(displayId);
+ dest.writeInt(sensorLocationX);
+ dest.writeInt(sensorLocationY);
+ dest.writeInt(sensorRadius);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<SensorLocationInternal> CREATOR =
+ new Creator<SensorLocationInternal>() {
+ @Override
+ public SensorLocationInternal createFromParcel(Parcel in) {
+ return new SensorLocationInternal(in);
+ }
+
+ @Override
+ public SensorLocationInternal[] newArray(int size) {
+ return new SensorLocationInternal[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "[id: " + displayId
+ + ", x: " + sensorLocationX
+ + ", y: " + sensorLocationY
+ + ", r: " + sensorRadius + "]";
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 45c6b29..4bf9a74 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -21,7 +21,9 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
@@ -38,34 +40,14 @@
*/
public final @FingerprintSensorProperties.SensorType int sensorType;
- /**
- * The location of the center of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
- * distance in pixels, measured from the left edge of the screen.
- */
- public final int sensorLocationX;
-
- /**
- * The location of the center of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
- * distance in pixels, measured from the top edge of the screen.
- *
- */
- public final int sensorLocationY;
-
- /**
- * The radius of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
- * of the sensor, in pixels.
- */
- public final int sensorRadius;
+ private final List<SensorLocationInternal> mSensorLocations;
public FingerprintSensorPropertiesInternal(int sensorId,
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@NonNull List<ComponentInfoInternal> componentInfo,
@FingerprintSensorProperties.SensorType int sensorType,
- boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
- int sensorRadius) {
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull List<SensorLocationInternal> sensorLocations) {
// IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not
// required as it can only be generated/attested/verified by TEE components.
// IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
@@ -73,9 +55,7 @@
super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.sensorLocationX = sensorLocationX;
- this.sensorLocationY = sensorLocationY;
- this.sensorRadius = sensorRadius;
+ this.mSensorLocations = List.copyOf(sensorLocations);
}
/**
@@ -88,16 +68,15 @@
boolean resetLockoutRequiresHardwareAuthToken) {
// TODO(b/179175438): Value should be provided from the HAL
this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType,
- resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
- 1636 /* sensorLocationY */, 130 /* sensorRadius */);
+ resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal(
+ "" /* displayId */, 540 /* sensorLocationX */, 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */)));
}
protected FingerprintSensorPropertiesInternal(Parcel in) {
super(in);
sensorType = in.readInt();
- sensorLocationX = in.readInt();
- sensorLocationY = in.readInt();
- sensorRadius = in.readInt();
+ mSensorLocations = in.createTypedArrayList(SensorLocationInternal.CREATOR);
}
public static final Creator<FingerprintSensorPropertiesInternal> CREATOR =
@@ -122,9 +101,7 @@
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
- dest.writeInt(sensorLocationX);
- dest.writeInt(sensorLocationY);
- dest.writeInt(sensorRadius);
+ dest.writeTypedList(mSensorLocations);
}
public boolean isAnyUdfpsType() {
@@ -150,6 +127,44 @@
}
}
+ /**
+ * Get the default location.
+ *
+ * Use this method when the sensor's relationship to the displays on the device do not
+ * matter.
+ * @return
+ */
+ @NonNull
+ public SensorLocationInternal getLocation() {
+ final SensorLocationInternal location = getLocation("" /* displayId */);
+ return location != null ? location : SensorLocationInternal.DEFAULT;
+ }
+
+ /**
+ * Get the location of a sensor relative to a physical display layout.
+ *
+ * @param displayId stable display id
+ * @return location or null if none is specified
+ */
+ @Nullable
+ public SensorLocationInternal getLocation(String displayId) {
+ for (SensorLocationInternal location : mSensorLocations) {
+ if (location.displayId.equals(displayId)) {
+ return location;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets all locations relative to all supported display layouts.
+ * @return supported locations
+ */
+ @NonNull
+ public List<SensorLocationInternal> getAllLocations() {
+ return mSensorLocations;
+ }
+
@Override
public String toString() {
return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index f18360ff..938431d 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -22,13 +22,6 @@
* @hide
*/
oneway interface IUdfpsOverlayController {
- const int REASON_UNKNOWN = 0;
- const int REASON_ENROLL_FIND_SENSOR = 1;
- const int REASON_ENROLL_ENROLLING = 2;
- const int REASON_AUTH_BP = 3; // BiometricPrompt
- const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard
- const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage
-
// Shows the overlay.
void showUdfpsOverlay(int sensorId, int reason, IUdfpsOverlayControllerCallback callback);
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index 0c21746..1959ff8 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -4,6 +4,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.hardware.hdmi.HdmiControlManager.VendorCommandListener;
+import android.hardware.hdmi.HdmiTvClient.SelectCallback;
import android.os.RemoteException;
import android.util.Log;
@@ -28,6 +29,36 @@
}
/**
+ * Selects a CEC logical device to be a new active source.
+ *
+ * @param logicalAddress logical address of the device to select
+ * @param callback callback to get the result with
+ * @throws {@link IllegalArgumentException} if the {@code callback} is null
+ */
+ public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback must not be null.");
+ }
+ try {
+ mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select device: ", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+ return new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ callback.onComplete(result);
+ }
+ };
+ }
+
+ /**
* Returns the active source information.
*
* @return {@link HdmiDeviceInfo} object that describes the active source
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index e1ed3ea..74cf001 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -77,7 +77,9 @@
* @param logicalAddress logical address of the device to select
* @param callback callback to get the result with
* @throws {@link IllegalArgumentException} if the {@code callback} is null
+ * @deprecated Please use {@link HdmiClient#deviceSelect()} instead.
*/
+ @Deprecated
public void deviceSelect(int logicalAddress, @NonNull SelectCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null.");
@@ -89,6 +91,10 @@
}
}
+ /**
+ * @deprecated Please use {@link HdmiClient#getCallbackWrapper()} instead.
+ */
+ @Deprecated
private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
return new IHdmiControlCallback.Stub() {
@Override
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 90990b4..e30594f 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -32,7 +32,6 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
@@ -189,19 +188,13 @@
final IInputContext inputContext = (IInputContext) args.arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
- SomeArgs moreArgs = (SomeArgs) args.arg5;
+ final boolean restarting = args.argi5 == 1;
final InputConnection ic = inputContext != null
- ? new RemoteInputConnection(
- mTarget, inputContext, moreArgs.argi3, cancellationGroup)
+ ? new RemoteInputConnection(mTarget, inputContext, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
- inputMethod.dispatchStartInputWithToken(
- ic,
- info,
- moreArgs.argi1 == 1 /* restarting */,
- startInputToken);
+ inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken);
args.recycle();
- moreArgs.recycle();
return;
}
case DO_CREATE_SESSION: {
@@ -299,11 +292,8 @@
Log.e(TAG, "bindInput must be paired with unbindInput.");
}
mCancellationGroup = new CancellationGroup();
- // This IInputContext is guaranteed to implement all the methods.
- final int missingMethodFlags = 0;
InputConnection ic = new RemoteInputConnection(mTarget,
- IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
- mCancellationGroup);
+ IInputContext.Stub.asInterface(binding.getConnectionToken()), mCancellationGroup);
InputBinding nu = new InputBinding(ic, binding);
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
}
@@ -324,17 +314,13 @@
@BinderThread
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
- @InputConnectionInspector.MissingMethodFlags final int missingMethods,
EditorInfo attribute, boolean restarting) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = restarting ? 1 : 0;
- args.argi3 = missingMethods;
- mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(DO_START_INPUT, startInputToken,
- inputContext, attribute, mCancellationGroup, args));
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
+ inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 464b421..657f842 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -22,14 +22,13 @@
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionInspector;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
@@ -62,11 +61,29 @@
@NonNull
private final IInputContextInvoker mInvoker;
- @NonNull
- private final WeakReference<InputMethodServiceInternal> mInputMethodService;
+ private static final class InputMethodServiceInternalHolder {
+ @NonNull
+ private final WeakReference<InputMethodServiceInternal> mServiceRef;
- @MissingMethodFlags
- private final int mMissingMethods;
+ private InputMethodServiceInternalHolder(
+ @NonNull WeakReference<InputMethodServiceInternal> ims) {
+ mServiceRef = ims;
+ }
+
+ @AnyThread
+ @Nullable
+ public InputMethodServiceInternal getAndWarnIfNull() {
+ final InputMethodServiceInternal ims = mServiceRef.get();
+ if (ims == null) {
+ Log.e(TAG, "InputMethodService is already destroyed. InputConnection instances"
+ + " cannot be used beyond InputMethodService lifetime.", new Throwable());
+ }
+ return ims;
+ }
+ }
+
+ @NonNull
+ private final InputMethodServiceInternalHolder mImsInternal;
/**
* Signaled when the system decided to take away IME focus from the target app.
@@ -79,11 +96,9 @@
RemoteInputConnection(
@NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
- IInputContext inputContext, @MissingMethodFlags int missingMethods,
- @NonNull CancellationGroup cancellationGroup) {
- mInputMethodService = inputMethodService;
+ IInputContext inputContext, @NonNull CancellationGroup cancellationGroup) {
+ mImsInternal = new InputMethodServiceInternalHolder(inputMethodService);
mInvoker = IInputContextInvoker.create(inputContext);
- mMissingMethods = missingMethods;
mCancellationGroup = cancellationGroup;
}
@@ -101,11 +116,11 @@
final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
flags, result);
- inputMethodService.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
}
return result;
@@ -125,11 +140,11 @@
final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
flags, result);
- inputMethodService.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
}
return result;
@@ -141,19 +156,15 @@
return null;
}
- if (isMethodMissing(MissingMethodFlags.GET_SELECTED_TEXT)) {
- // This method is not implemented.
- return null;
- }
final CompletableFuture<CharSequence> value = mInvoker.getSelectedText(flags);
final CharSequence result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
result);
- inputMethodService.triggerServiceDump(TAG + "#getSelectedText", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getSelectedText", icProto);
}
return result;
@@ -178,20 +189,16 @@
return null;
}
- if (isMethodMissing(MissingMethodFlags.GET_SURROUNDING_TEXT)) {
- // This method is not implemented.
- return null;
- }
final CompletableFuture<SurroundingText> value = mInvoker.getSurroundingText(beforeLength,
afterLength, flags);
final SurroundingText result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
beforeLength, afterLength, flags, result);
- inputMethodService.triggerServiceDump(TAG + "#getSurroundingText", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getSurroundingText", icProto);
}
return result;
@@ -207,11 +214,11 @@
final int result = CompletableFutureUtil.getResultOrZero(
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
reqModes, result);
- inputMethodService.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
}
return result;
@@ -227,11 +234,11 @@
final ExtractedText result = CompletableFutureUtil.getResultOrNull(
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal != null && ImeTracing.getInstance().isEnabled()) {
final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
request, flags, result);
- inputMethodService.triggerServiceDump(TAG + "#getExtractedText", icProto);
+ imsInternal.triggerServiceDump(TAG + "#getExtractedText", icProto);
}
return result;
@@ -248,12 +255,11 @@
@AnyThread
private void notifyUserActionIfNecessary() {
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService == null) {
- // This basically should not happen, because it's the the caller of this method.
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal == null) {
return;
}
- inputMethodService.notifyUserActionIfNecessary();
+ imsInternal.notifyUserActionIfNecessary();
}
@AnyThread
@@ -263,10 +269,6 @@
@AnyThread
public boolean commitCorrection(CorrectionInfo correctionInfo) {
- if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
- // This method is not implemented.
- return false;
- }
return mInvoker.commitCorrection(correctionInfo);
}
@@ -287,10 +289,6 @@
@AnyThread
public boolean setComposingRegion(int start, int end) {
- if (isMethodMissing(MissingMethodFlags.SET_COMPOSING_REGION)) {
- // This method is not implemented.
- return false;
- }
return mInvoker.setComposingRegion(start, end);
}
@@ -339,10 +337,6 @@
@AnyThread
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
- if (isMethodMissing(MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS)) {
- // This method is not implemented.
- return false;
- }
return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
}
@@ -368,11 +362,14 @@
return false;
}
- if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
- // This method is not implemented.
+ final InputMethodServiceInternal ims = mImsInternal.getAndWarnIfNull();
+ if (ims == null) {
return false;
}
- final CompletableFuture<Boolean> value = mInvoker.requestCursorUpdates(cursorUpdateMode);
+
+ final int displayId = ims.getContext().getDisplayId();
+ final CompletableFuture<Boolean> value =
+ mInvoker.requestCursorUpdates(cursorUpdateMode, displayId);
return CompletableFutureUtil.getResultOrFalse(value, TAG, "requestCursorUpdates()",
mCancellationGroup, MAX_WAIT_TIME_MILLIS);
}
@@ -394,18 +391,12 @@
return false;
}
- if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
- // This method is not implemented.
- return false;
- }
-
if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
- if (inputMethodService == null) {
- // This basically should not happen, because it's the caller of this method.
+ final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
+ if (imsInternal == null) {
return false;
}
- inputMethodService.exposeContent(inputContentInfo, this);
+ imsInternal.exposeContent(inputContentInfo, this);
}
final CompletableFuture<Boolean> value =
@@ -423,16 +414,9 @@
}
@AnyThread
- private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
- return (mMissingMethods & methodFlag) == methodFlag;
- }
-
- @AnyThread
@Override
public String toString() {
return "RemoteInputConnection{idHash=#"
- + Integer.toHexString(System.identityHashCode(this))
- + " mMissingMethods="
- + InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
+ + Integer.toHexString(System.identityHashCode(this)) + "}";
}
}
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 68917a8..08f75df 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@
import android.util.BackupUtils;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkIdentityUtils;
@@ -151,24 +150,6 @@
}
}
- private static boolean sForceAllNetworkTypes = false;
-
- /**
- * Results in matching against all mobile network types.
- *
- * <p>See {@link #matchesMobile} and {@link matchesMobileWildcard}.
- */
- @VisibleForTesting
- public static void forceAllNetworkTypes() {
- sForceAllNetworkTypes = true;
- }
-
- /** Resets the affect of {@link #forceAllNetworkTypes}. */
- @VisibleForTesting
- public static void resetForceAllNetworkTypes() {
- sForceAllNetworkTypes = false;
- }
-
/**
* Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
* the given IMSI.
@@ -611,7 +592,7 @@
// Only metered mobile network would be matched regardless of metered filter.
// This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
// TODO: Respect metered filter and remove mMetered condition.
- return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+ return (ident.mType == TYPE_MOBILE && ident.mMetered)
&& !ArrayUtils.isEmpty(mMatchSubscriberIds)
&& ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
&& matchesCollapsedRatType(ident);
@@ -726,7 +707,7 @@
if (ident.mType == TYPE_WIMAX) {
return true;
} else {
- return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+ return (ident.mType == TYPE_MOBILE && ident.mMetered)
&& matchesCollapsedRatType(ident);
}
}
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index 4d160da..d83d94a 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -40,4 +40,9 @@
* @see SystemConfigManager#getSystemPermissionUids
*/
int[] getSystemPermissionUids(String permissionName);
+
+ /**
+ * @see SystemConfigManager#getEnabledComponentOverrides
+ */
+ List<String> getEnabledComponentOverrides(String packageName);
}
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 9bfa8ad..a6316df 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -17,6 +17,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -129,4 +130,21 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Get enabled component for a specific package
+ *
+ * @param packageName The target package.
+ * @return The enabled component
+ * {@hide}
+ */
+ @SystemApi
+ @NonNull
+ public List<String> getEnabledComponentOverrides(@NonNull String packageName) {
+ try {
+ return mInterface.getEnabledComponentOverrides(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 4cd9fb9..db9d4e2 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -37,6 +37,7 @@
import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -80,6 +81,26 @@
public final class PermissionManager {
private static final String LOG_TAG = PermissionManager.class.getName();
+ /**
+ * The permission is granted.
+ */
+ public static final int PERMISSION_GRANTED = 0;
+
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ * <p>
+ * The app isn't expecting the permission to be denied so that a "no-op" action should be taken,
+ * such as returning an empty result.
+ */
+ public static final int PERMISSION_SOFT_DENIED = 1;
+
+ /**
+ * The permission is denied.
+ * <p>
+ * The app should receive a {@code SecurityException}, or an error through a relevant callback.
+ */
+ public static final int PERMISSION_HARD_DENIED = 2;
+
/** @hide */
public static final String LOG_TAG_TRACE_GRANTS = "PermissionGrantTrace";
@@ -164,6 +185,69 @@
}
/**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(String, AttributionSource)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource the permission identity
+ * @param message A message describing the reason the permission was checked
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ *
+ * @see #checkPermissionForPreflight(String, AttributionSource)
+ */
+ @PermissionCheckerManager.PermissionResult
+ public int checkPermissionForDataDelivery(@NonNull String permission,
+ @NonNull AttributionSource attributionSource, @Nullable String message) {
+ return PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
+ // FIXME(b/199526514): PID should be passed inside AttributionSource.
+ PermissionChecker.PID_UNKNOWN, attributionSource, message);
+ }
+
+ /**
+ * Checks whether a given data access chain described by the given {@link AttributionSource}
+ * has a given permission.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a data listener it should have the required
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have the
+ * permission and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the protected data to a registered
+ * listener you should use {@link #checkPermissionForDataDelivery(String,
+ * AttributionSource, String)} which will evaluate the permission access based
+ * on the current fg/bg state of the app and leave a record that the data was accessed.
+ *
+ * @param permission The permission to check.
+ * @param attributionSource The identity for which to check the permission.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
+ */
+ @PermissionCheckerManager.PermissionResult
+ public int checkPermissionForPreflight(@NonNull String permission,
+ @NonNull AttributionSource attributionSource) {
+ return PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ attributionSource);
+ }
+
+ /**
* Retrieve all of the information we know about a particular permission.
*
* @param permissionName the fully qualified name (e.g. com.android.permission.LOGIN) of the
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 8e4a68e..4004148 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -436,7 +436,7 @@
try {
ApplicationInfo ai = context.getPackageManager()
.getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
- getUserId());
+ getNormalizedUserId());
mContext = context.createApplicationContext(ai,
Context.CONTEXT_RESTRICTED);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 362ea8c..5e647a4 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -115,7 +115,7 @@
@NonNull AttributionSource attributionSource) {
try {
if (mCurrentCallback == null) {
- boolean preflightPermissionCheckPassed = checkPermissionForPreflight(
+ boolean preflightPermissionCheckPassed = checkPermissionForPreflightNotHardDenied(
attributionSource);
if (preflightPermissionCheckPassed) {
if (DBG) {
@@ -470,10 +470,11 @@
return mStartedDataDelivery;
}
- private boolean checkPermissionForPreflight(AttributionSource attributionSource) {
- return PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
- Manifest.permission.RECORD_AUDIO, attributionSource)
- == PermissionChecker.PERMISSION_GRANTED;
+ private boolean checkPermissionForPreflightNotHardDenied(AttributionSource attributionSource) {
+ int result = PermissionChecker.checkPermissionForPreflight(RecognitionService.this,
+ Manifest.permission.RECORD_AUDIO, attributionSource);
+ return result == PermissionChecker.PERMISSION_GRANTED
+ || result == PermissionChecker.PERMISSION_SOFT_DENIED;
}
void finishDataDelivery() {
diff --git a/core/java/android/view/InsetsVisibilities.java b/core/java/android/view/InsetsVisibilities.java
index 30668ba..7d259fb 100644
--- a/core/java/android/view/InsetsVisibilities.java
+++ b/core/java/android/view/InsetsVisibilities.java
@@ -116,6 +116,10 @@
dest.writeIntArray(mVisibilities);
}
+ public void readFromParcel(@NonNull Parcel in) {
+ in.readIntArray(mVisibilities);
+ }
+
public static final @NonNull Creator<InsetsVisibilities> CREATOR =
new Creator<InsetsVisibilities>() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index ddc532c..945758f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1464,19 +1464,18 @@
@Override
public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
- if (mSurfaceControl == null) {
- return;
- }
-
- // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
- // its 2nd frame if RenderThread is running slowly could potentially see
- // this as false, enter the branch, get pre-empted, then this comes along
- // and reports a new position, then the UI thread resumes and reports
- // its position. This could therefore be de-sync'd in that interval, but
- // the synchronization would violate the rule that RT must never block
- // on the UI thread which would open up potential deadlocks. The risk of
- // a single-frame desync is therefore preferable for now.
synchronized(mSurfaceControlLock) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+ // its 2nd frame if RenderThread is running slowly could potentially see
+ // this as false, enter the branch, get pre-empted, then this comes along
+ // and reports a new position, then the UI thread resumes and reports
+ // its position. This could therefore be de-sync'd in that interval, but
+ // the synchronization would violate the rule that RT must never block
+ // on the UI thread which would open up potential deadlocks. The risk of
+ // a single-frame desync is therefore preferable for now.
mRtHandlingPositionUpdates = true;
}
if (mRTLastReportedPosition.left == left
@@ -1506,8 +1505,11 @@
if (mViewVisibility) {
mPositionChangedTransaction.show(mSurfaceControl);
}
- applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
- getViewRootImpl().mSurface, frameNumber);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
+ viewRoot.mSurface, frameNumber);
+ }
applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
mPendingTransaction = false;
} catch (Exception ex) {
@@ -1528,7 +1530,7 @@
@Override
public void positionLost(long frameNumber) {
- if (DEBUG) {
+ if (DEBUG_POSITION) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
}
@@ -1540,15 +1542,15 @@
mPositionChangedTransaction.clear();
mPendingTransaction = false;
}
- if (mSurfaceControl == null) {
- return;
- }
/**
* positionLost can be called while UI thread is un-paused so we
* need to hold the lock here.
*/
synchronized (mSurfaceControlLock) {
+ if (mSurfaceControl == null) {
+ return;
+ }
mRtTransaction.hide(mSurfaceControl);
if (mRtReleaseSurfaces) {
mRtReleaseSurfaces = false;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 5185dc2..aa28a92 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -274,10 +274,7 @@
*
* @param flags Supplies additional options controlling how the text is
* returned. May be either {@code 0} or {@link #GET_TEXT_WITH_STYLES}.
- * @return the text that is currently selected, if any, or null if
- * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
- * later, returns false when the target application does not implement
- * this method.
+ * @return the text that is currently selected, if any, or {@code null} if no text is selected.
*/
CharSequence getSelectedText(int flags);
@@ -483,8 +480,9 @@
* If this is greater than the number of existing characters between the cursor and
* the end of the text, then this method does not fail but deletes all the characters in
* that range.
- * @return true on success, false if the input connection is no longer valid. Returns
- * {@code false} when the target application does not implement this method.
+ * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+ * Before Android {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned
+ * {@code false} when the target application does not implement this method.
*/
boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
@@ -573,9 +571,10 @@
*
* @param start the position in the text at which the composing region begins
* @param end the position in the text at which the composing region ends
- * @return true on success, false if the input connection is no longer
- * valid. In {@link android.os.Build.VERSION_CODES#N} and later, false is returned when the
- * target application does not implement this method.
+ * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+ * Since Android {@link android.os.Build.VERSION_CODES#N} until
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+ * the target application does not implement this method.
*/
boolean setComposingRegion(int start, int end);
@@ -686,9 +685,10 @@
* in progress.</p>
*
* @param correctionInfo Detailed information about the correction.
- * @return true on success, false if the input connection is no longer valid.
- * In {@link android.os.Build.VERSION_CODES#N} and later, returns false
- * when the target application does not implement this method.
+ * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+ * Since Android {@link android.os.Build.VERSION_CODES#N} until
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+ * the target application does not implement this method.
*/
boolean commitCorrection(CorrectionInfo correctionInfo);
@@ -924,10 +924,11 @@
* {@link #CURSOR_UPDATE_MONITOR}. Pass {@code 0} to disable the effect of
* {@link #CURSOR_UPDATE_MONITOR}.
* @return {@code true} if the request is scheduled. {@code false} to indicate that when the
- * application will not call
- * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}.
- * In {@link android.os.Build.VERSION_CODES#N} and later, returns {@code false} also when the
- * target application does not implement this method.
+ * application will not call {@link InputMethodManager#updateCursorAnchorInfo(
+ * android.view.View, CursorAnchorInfo)}.
+ * Since Android {@link android.os.Build.VERSION_CODES#N} until
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+ * the target application does not implement this method.
*/
boolean requestCursorUpdates(int cursorUpdateMode);
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
deleted file mode 100644
index 7621da7..0000000
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ /dev/null
@@ -1,293 +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 android.view.inputmethod;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-
-import java.lang.annotation.Retention;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * @hide
- */
-public final class InputConnectionInspector {
-
- @Retention(SOURCE)
- @IntDef({MissingMethodFlags.GET_SELECTED_TEXT,
- MissingMethodFlags.SET_COMPOSING_REGION,
- MissingMethodFlags.COMMIT_CORRECTION,
- MissingMethodFlags.REQUEST_CURSOR_UPDATES,
- MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
- MissingMethodFlags.GET_HANDLER,
- MissingMethodFlags.CLOSE_CONNECTION,
- MissingMethodFlags.COMMIT_CONTENT,
- MissingMethodFlags.GET_SURROUNDING_TEXT
- })
- public @interface MissingMethodFlags {
- /**
- * {@link InputConnection#getSelectedText(int)} is available in
- * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
- */
- int GET_SELECTED_TEXT = 1 << 0;
- /**
- * {@link InputConnection#setComposingRegion(int, int)} is available in
- * {@link android.os.Build.VERSION_CODES#GINGERBREAD} and later.
- */
- int SET_COMPOSING_REGION = 1 << 1;
- /**
- * {@link InputConnection#commitCorrection(CorrectionInfo)} is available in
- * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and later.
- */
- int COMMIT_CORRECTION = 1 << 2;
- /**
- * {@link InputConnection#requestCursorUpdates(int)} is available in
- * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
- */
- int REQUEST_CURSOR_UPDATES = 1 << 3;
- /**
- * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
- * {@link android.os.Build.VERSION_CODES#N} and later.
- */
- int DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 1 << 4;
- /**
- * {@link InputConnection#deleteSurroundingTextInCodePoints(int, int)}} is available in
- * {@link android.os.Build.VERSION_CODES#N} and later.
- */
- int GET_HANDLER = 1 << 5;
- /**
- * {@link InputConnection#closeConnection()}} is available in
- * {@link android.os.Build.VERSION_CODES#N} and later.
- */
- int CLOSE_CONNECTION = 1 << 6;
- /**
- * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} is available in
- * {@link android.os.Build.VERSION_CODES#N} MR-1 and later.
- */
- int COMMIT_CONTENT = 1 << 7;
- /**
- * {@link InputConnection#getSurroundingText(int, int, int)} is available in
- * {@link android.os.Build.VERSION_CODES#S} and later.
- */
- int GET_SURROUNDING_TEXT = 1 << 8;
- }
-
- private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
- new WeakHashMap<>());
-
- @MissingMethodFlags
- public static int getMissingMethodFlags(@Nullable final InputConnection ic) {
- if (ic == null) {
- return 0;
- }
- // Optimization for a known class.
- if (ic instanceof BaseInputConnection) {
- return 0;
- }
- // Optimization for a known class.
- if (ic instanceof InputConnectionWrapper) {
- return ((InputConnectionWrapper) ic).getMissingMethodFlags();
- }
- return getMissingMethodFlagsInternal(ic.getClass());
- }
-
- @MissingMethodFlags
- public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
- final Integer cachedFlags = sMissingMethodsMap.get(clazz);
- if (cachedFlags != null) {
- return cachedFlags;
- }
- int flags = 0;
- if (!hasGetSelectedText(clazz)) {
- flags |= MissingMethodFlags.GET_SELECTED_TEXT;
- }
- if (!hasSetComposingRegion(clazz)) {
- flags |= MissingMethodFlags.SET_COMPOSING_REGION;
- }
- if (!hasCommitCorrection(clazz)) {
- flags |= MissingMethodFlags.COMMIT_CORRECTION;
- }
- if (!hasRequestCursorUpdate(clazz)) {
- flags |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
- }
- if (!hasDeleteSurroundingTextInCodePoints(clazz)) {
- flags |= MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS;
- }
- if (!hasGetHandler(clazz)) {
- flags |= MissingMethodFlags.GET_HANDLER;
- }
- if (!hasCloseConnection(clazz)) {
- flags |= MissingMethodFlags.CLOSE_CONNECTION;
- }
- if (!hasCommitContent(clazz)) {
- flags |= MissingMethodFlags.COMMIT_CONTENT;
- }
- if (!hasGetSurroundingText(clazz)) {
- flags |= MissingMethodFlags.GET_SURROUNDING_TEXT;
- }
- sMissingMethodsMap.put(clazz, flags);
- return flags;
- }
-
- private static boolean hasGetSelectedText(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("getSelectedText", int.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasSetComposingRegion(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("setComposingRegion", int.class, int.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasCommitCorrection(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("commitCorrection", CorrectionInfo.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasRequestCursorUpdate(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("requestCursorUpdates", int.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasDeleteSurroundingTextInCodePoints(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("deleteSurroundingTextInCodePoints", int.class,
- int.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasGetHandler(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("getHandler");
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasCloseConnection(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("closeConnection");
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasCommitContent(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("commitContent", InputContentInfo.class,
- int.class, Bundle.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- private static boolean hasGetSurroundingText(@NonNull final Class clazz) {
- try {
- final Method method = clazz.getMethod("getSurroundingText", int.class, int.class,
- int.class);
- return !Modifier.isAbstract(method.getModifiers());
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
- final StringBuilder sb = new StringBuilder();
- boolean isEmpty = true;
- if ((flags & MissingMethodFlags.GET_SELECTED_TEXT) != 0) {
- sb.append("getSelectedText(int)");
- isEmpty = false;
- }
- if ((flags & MissingMethodFlags.SET_COMPOSING_REGION) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("setComposingRegion(int, int)");
- isEmpty = false;
- }
- if ((flags & MissingMethodFlags.COMMIT_CORRECTION) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("commitCorrection(CorrectionInfo)");
- isEmpty = false;
- }
- if ((flags & MissingMethodFlags.REQUEST_CURSOR_UPDATES) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("requestCursorUpdate(int)");
- isEmpty = false;
- }
- if ((flags & MissingMethodFlags.DELETE_SURROUNDING_TEXT_IN_CODE_POINTS) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("deleteSurroundingTextInCodePoints(int, int)");
- isEmpty = false;
- }
- if ((flags & MissingMethodFlags.GET_HANDLER) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("getHandler()");
- }
- if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("closeConnection()");
- }
- if ((flags & MissingMethodFlags.COMMIT_CONTENT) != 0) {
- if (!isEmpty) {
- sb.append(",");
- }
- sb.append("commitContent(InputContentInfo, Bundle)");
- }
- return sb.toString();
- }
-}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index b1501a4..a99e9b8 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -30,8 +30,6 @@
public class InputConnectionWrapper implements InputConnection {
private InputConnection mTarget;
final boolean mMutable;
- @InputConnectionInspector.MissingMethodFlags
- private int mMissingMethodFlags;
/**
* Initializes a wrapper.
@@ -46,7 +44,6 @@
public InputConnectionWrapper(InputConnection target, boolean mutable) {
mMutable = mutable;
mTarget = target;
- mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
}
/**
@@ -63,15 +60,6 @@
throw new SecurityException("not mutable");
}
mTarget = target;
- mMissingMethodFlags = InputConnectionInspector.getMissingMethodFlags(target);
- }
-
- /**
- * @hide
- */
- @InputConnectionInspector.MissingMethodFlags
- public int getMissingMethodFlags() {
- return mMissingMethodFlags;
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 139b69c..009afce 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -683,7 +683,6 @@
windowFlags,
null,
null,
- 0 /* missingMethodFlags */,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1938,7 +1937,6 @@
mServedInputConnection = null;
}
RemoteInputConnectionImpl servedInputConnection;
- final int missingMethodFlags;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
mCursorSelEnd = tba.initialSelEnd;
@@ -1946,19 +1944,17 @@
mCursorCandEnd = -1;
mCursorRect.setEmpty();
mCursorAnchorInfo = null;
- missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
- if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
- != 0) {
- // InputConnection#getHandler() is not implemented.
- icHandler = null;
- } else {
- icHandler = ic.getHandler();
+ Handler handler = null;
+ try {
+ handler = ic.getHandler();
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
}
+ icHandler = handler;
servedInputConnection = new RemoteInputConnectionImpl(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
} else {
servedInputConnection = null;
- missingMethodFlags = 0;
icHandler = null;
}
mServedInputConnection = servedInputConnection;
@@ -1971,7 +1967,7 @@
try {
res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
- softInputMode, windowFlags, tba, servedInputConnection, missingMethodFlags,
+ softInputMode, windowFlags, tba, servedInputConnection,
view.getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4a65511..a07141b 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -376,6 +376,8 @@
* must be used to inform the WebView of the new quota.
* @deprecated This method is no longer called; WebView now uses the HTML5 / JavaScript Quota
* Management API.
+ * @removed This method is no longer called; WebView now uses the HTML5 / JavaScript Quota
+ * Management API.
*/
@Deprecated
public void onReachedMaxAppCacheSize(long requiredStorage, long quota,
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 9b753f1..364ba50 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1131,8 +1131,12 @@
* become a no-op on all Android versions once support is
* removed in Chromium. Consider using Service Workers instead.
* See https://web.dev/appcache-removal/ for more information.
+ * @removed The Application Cache API is no longer supported and this method
+ * is a no-op on WebView 95 and later. Consider using Service Workers
+ * instead. See https://web.dev/appcache-removal/ for more information.
*/
- public abstract void setAppCacheEnabled(boolean flag);
+ @Deprecated
+ public void setAppCacheEnabled(boolean flag) {}
/**
* Sets the path to the Application Caches files. In order for the
@@ -1147,8 +1151,12 @@
* become a no-op on all Android versions once support is
* removed in Chromium. Consider using Service Workers instead.
* See https://web.dev/appcache-removal/ for more information.
+ * @removed The Application Cache API is no longer supported and this method
+ * is a no-op on WebView 95 and later. Consider using Service Workers
+ * instead. See https://web.dev/appcache-removal/ for more information.
*/
- public abstract void setAppCachePath(String appCachePath);
+ @Deprecated
+ public void setAppCachePath(String appCachePath) {}
/**
* Sets the maximum size for the Application Cache content. The passed size
@@ -1160,9 +1168,10 @@
*
* @param appCacheMaxSize the maximum size in bytes
* @deprecated Quota is managed automatically; this method is a no-op.
+ * @removed Quota is managed automatically; this method is a no-op.
*/
@Deprecated
- public abstract void setAppCacheMaxSize(long appCacheMaxSize);
+ public void setAppCacheMaxSize(long appCacheMaxSize) {}
/**
* Sets whether the database storage API is enabled. The default value is
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index 947d0cb..cd63810 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -22,18 +22,12 @@
/**
* This class is used to manage the JavaScript storage APIs provided by the
- * {@link WebView}. It manages the Application Cache API, the Web SQL Database
- * API and the HTML5 Web Storage API.
- *
- * The Application Cache API provides a mechanism to create and maintain an
- * application cache to power offline Web applications. Use of the Application
- * Cache API can be attributed to an origin {@link WebStorage.Origin}, however
- * it is not possible to set per-origin quotas. Note that there can be only
- * one application cache per application.
+ * {@link WebView}. It manages the Web SQL Database API and the HTML5 Web
+ * Storage API.
*
* The Web SQL Database API provides storage which is private to a given origin.
- * Similar to the Application Cache, use of the Web SQL Database can be attributed
- * to an origin. It is also possible to set per-origin quotas.
+ * Use of the Web SQL Database can be attributed to an origin. It is also
+ * possible to set per-origin quotas.
*/
public class WebStorage {
@@ -41,8 +35,7 @@
* Encapsulates a callback function which is used to provide a new quota
* for a JavaScript storage API.
* See
- * {@link WebChromeClient#onExceededDatabaseQuota} and
- * {@link WebChromeClient#onReachedMaxAppCacheSize}.
+ * {@link WebChromeClient#onExceededDatabaseQuota}.
* @deprecated This class is obsolete and no longer used.
*/
@Deprecated
@@ -123,9 +116,9 @@
*/
/**
- * Gets the origins currently using either the Application Cache or Web SQL
- * Database APIs. This method operates asynchronously, with the result
- * being provided via a {@link ValueCallback}. The origins are provided as
+ * Gets the origins currently using the Web SQL Database APIs. This method
+ * operates asynchronously, with the result being provided via a
+ * {@link ValueCallback}. The origins are provided as
* a map, of type {@code Map<String, WebStorage.Origin>}, from the string
* representation of the origin to a {@link WebStorage.Origin} object.
*/
@@ -134,9 +127,9 @@
}
/**
- * Gets the amount of storage currently being used by both the Application
- * Cache and Web SQL Database APIs by the given origin. The amount is given
- * in bytes and the origin is specified using its string representation.
+ * Gets the amount of storage currently being used by the Web SQL Database
+ * APIs by the given origin. The amount is given in bytes and the origin
+ * is specified using its string representation.
* This method operates asynchronously, with the result being provided via
* a {@link ValueCallback}.
*/
@@ -148,8 +141,7 @@
* Gets the storage quota for the Web SQL Database API for the given origin.
* The quota is given in bytes and the origin is specified using its string
* representation. This method operates asynchronously, with the result
- * being provided via a {@link ValueCallback}. Note that a quota is not
- * enforced on a per-origin basis for the Application Cache API.
+ * being provided via a {@link ValueCallback}.
*/
public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
// Must be a no-op for backward compatibility: see the hidden constructor for reason.
@@ -158,8 +150,7 @@
/**
* Sets the storage quota for the Web SQL Database API for the given origin.
* The quota is specified in bytes and the origin is specified using its string
- * representation. Note that a quota is not enforced on a per-origin basis
- * for the Application Cache API.
+ * representation.
* @deprecated Controlling quota per-origin will not be supported in future.
*/
@Deprecated
@@ -168,9 +159,8 @@
}
/**
- * Clears the storage currently being used by both the Application Cache and
- * Web SQL Database APIs by the given origin. The origin is specified using
- * its string representation.
+ * Clears the storage currently being used by the Web SQL Database APIs by
+ * the given origin. The origin is specified using its string representation.
*/
public void deleteOrigin(String origin) {
// Must be a no-op for backward compatibility: see the hidden constructor for reason.
@@ -178,8 +168,7 @@
/**
* Clears all storage currently being used by the JavaScript storage APIs.
- * This includes the Application Cache, Web SQL Database and the HTML5 Web
- * Storage APIs.
+ * This includes Web SQL Database and the HTML5 Web Storage APIs.
*/
public void deleteAllData() {
// Must be a no-op for backward compatibility: see the hidden constructor for reason.
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 8b8dba8..69bc1b5 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -88,4 +88,9 @@
* user has pressed back on the root activity of a task controlled by the task organizer.
*/
void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
+
+ /**
+ * Called when the IME has drawn on the organized task.
+ */
+ void onImeDrawnOnTask(int taskId);
}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 8c64474..10d21a0 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -26,6 +26,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.WindowManager;
/**
@@ -165,7 +166,13 @@
* TaskSnapshot.
* @hide
*/
- public TaskSnapshot mTaskSnapshot;
+ public TaskSnapshot taskSnapshot;
+
+ /**
+ * The requested insets visibility of the top main window.
+ * @hide
+ */
+ public final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
public StartingWindowInfo() {
@@ -190,7 +197,8 @@
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
dest.writeBoolean(isKeyguardOccluded);
- dest.writeTypedObject(mTaskSnapshot, flags);
+ dest.writeTypedObject(taskSnapshot, flags);
+ requestedVisibilities.writeToParcel(dest, flags);
}
void readFromParcel(@NonNull Parcel source) {
@@ -203,7 +211,8 @@
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
isKeyguardOccluded = source.readBoolean();
- mTaskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
+ taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
+ requestedVisibilities.readFromParcel(source);
}
@Override
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 6f250fc..845c13d 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -144,6 +144,10 @@
@BinderThread
public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
+ /** @hide */
+ @BinderThread
+ public void onImeDrawnOnTask(int taskId) {}
+
/**
* Creates a persistent root task in WM for a particular windowing-mode.
* @param displayId The display to create the root task on.
@@ -288,6 +292,11 @@
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
}
+
+ @Override
+ public void onImeDrawnOnTask(int taskId) {
+ mExecutor.execute(() -> TaskOrganizer.this.onImeDrawnOnTask(taskId));
+ }
};
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index c2ffc03..7208930 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -140,7 +140,7 @@
private TransitionInfo(Parcel in) {
mType = in.readInt();
mFlags = in.readInt();
- in.readList(mChanges, null /* classLoader */);
+ in.readTypedList(mChanges, Change.CREATOR);
mRootLeash = new SurfaceControl();
mRootLeash.readFromParcel(in);
mRootOffset.readFromParcel(in);
@@ -152,7 +152,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeInt(mFlags);
- dest.writeList(mChanges);
+ dest.writeTypedList(mChanges);
mRootLeash.writeToParcel(dest, flags);
mRootOffset.writeToParcel(dest, flags);
dest.writeTypedObject(mOptions, flags);
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 387837d..00bc202 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -61,7 +61,7 @@
private WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
- in.readList(mHierarchyOps, null /* loader */);
+ in.readTypedList(mHierarchyOps, HierarchyOp.CREATOR);
mErrorCallbackToken = in.readStrongBinder();
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
}
@@ -512,17 +512,16 @@
* @param fragmentToken2 client assigned unique token to create TaskFragment with specified
* in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is
* {@code null}, the transaction will reset the adjacent TaskFragment.
- * @hide
*/
@NonNull
public WindowContainerTransaction setAdjacentTaskFragments(
@NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2,
- @Nullable TaskFragmentAdjacentOptions options) {
+ @Nullable TaskFragmentAdjacentParams params) {
final HierarchyOp hierarchyOp =
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
.setContainer(fragmentToken1)
.setReparentContainer(fragmentToken2)
- .setLaunchOptions(options != null ? options.toBundle() : null)
+ .setLaunchOptions(params != null ? params.toBundle() : null)
.build();
mHierarchyOps.add(hierarchyOp);
return this;
@@ -643,7 +642,7 @@
/** @hide */
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mChanges);
- dest.writeList(mHierarchyOps);
+ dest.writeTypedList(mHierarchyOps);
dest.writeStrongBinder(mErrorCallbackToken);
dest.writeStrongInterface(mTaskFragmentOrganizer);
}
@@ -916,7 +915,7 @@
* Changes because they must be executed in the same order that they are added.
* @hide
*/
- public static class HierarchyOp implements Parcelable {
+ public static final class HierarchyOp implements Parcelable {
public static final int HIERARCHY_OP_TYPE_REPARENT = 0;
public static final int HIERARCHY_OP_TYPE_REORDER = 1;
public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
@@ -1304,9 +1303,8 @@
/**
* Helper class for building an options Bundle that can be used to set adjacent rules of
* TaskFragments.
- * @hide
*/
- public static class TaskFragmentAdjacentOptions {
+ public static class TaskFragmentAdjacentParams {
private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL =
"android:transaction.adjacent.option.delay_primary_removal";
private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL =
@@ -1315,29 +1313,43 @@
private boolean mDelayPrimaryLastActivityRemoval;
private boolean mDelaySecondaryLastActivityRemoval;
- public TaskFragmentAdjacentOptions() {
+ public TaskFragmentAdjacentParams() {
}
- public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) {
+ public TaskFragmentAdjacentParams(@NonNull Bundle bundle) {
mDelayPrimaryLastActivityRemoval = bundle.getBoolean(
DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL);
mDelaySecondaryLastActivityRemoval = bundle.getBoolean(
DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL);
}
- public void setDelayPrimaryLastActivityRemoval(boolean delay) {
+ /** @see #shouldDelayPrimaryLastActivityRemoval() */
+ public void setShouldDelayPrimaryLastActivityRemoval(boolean delay) {
mDelayPrimaryLastActivityRemoval = delay;
}
- public void setDelaySecondaryLastActivityRemoval(boolean delay) {
+ /** @see #shouldDelaySecondaryLastActivityRemoval() */
+ public void setShouldDelaySecondaryLastActivityRemoval(boolean delay) {
mDelaySecondaryLastActivityRemoval = delay;
}
- public boolean isDelayPrimaryLastActivityRemoval() {
+ /**
+ * Whether to delay the last activity of the primary adjacent TaskFragment being immediately
+ * removed while finishing.
+ * <p>
+ * It is usually set to {@code true} to give organizer an opportunity to perform other
+ * actions or animations. An example is to finish together with the adjacent TaskFragment.
+ * </p>
+ */
+ public boolean shouldDelayPrimaryLastActivityRemoval() {
return mDelayPrimaryLastActivityRemoval;
}
- public boolean isDelaySecondaryLastActivityRemoval() {
+ /**
+ * Similar to {@link #shouldDelayPrimaryLastActivityRemoval()}, but for the secondary
+ * TaskFragment.
+ */
+ public boolean shouldDelaySecondaryLastActivityRemoval() {
return mDelaySecondaryLastActivityRemoval;
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 0b92b93..874e3f4 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -47,6 +47,8 @@
public class AccessibilityShortcutChooserActivity extends Activity {
@ShortcutType
private final int mShortcutType = ACCESSIBILITY_SHORTCUT_KEY;
+ private static final String KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE =
+ "accessibility_shortcut_menu_mode";
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
private AlertDialog mMenuDialog;
private AlertDialog mPermissionDialog;
@@ -66,14 +68,30 @@
mMenuDialog = createMenuDialog();
mMenuDialog.setOnShowListener(dialog -> updateDialogListeners());
mMenuDialog.show();
+
+ if (savedInstanceState != null) {
+ final int restoreShortcutMenuMode =
+ savedInstanceState.getInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE,
+ ShortcutMenuMode.LAUNCH);
+ if (restoreShortcutMenuMode == ShortcutMenuMode.EDIT) {
+ onEditButtonClicked();
+ }
+ }
}
@Override
protected void onDestroy() {
+ mMenuDialog.setOnDismissListener(null);
mMenuDialog.dismiss();
super.onDestroy();
}
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE, mTargetAdapter.getShortcutMenuMode());
+ }
+
private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
final AccessibilityTarget target = mTargets.get(position);
target.onSelected();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 019126f..a727b34 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1163,7 +1163,9 @@
-1);
// Action bar is user-independent, always start as primary
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
- finish();
+ if (!mAwaitingDelegateResponse) {
+ finish();
+ }
}
);
b.setId(R.id.chooser_nearby_button);
@@ -1185,7 +1187,9 @@
-1);
// Action bar is user-independent, always start as primary
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
- finish();
+ if (!mAwaitingDelegateResponse) {
+ finish();
+ }
}
);
b.setId(R.id.chooser_edit_button);
@@ -2212,7 +2216,9 @@
TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
if (super.onTargetSelected(clonedTarget, false)) {
updateModelAndChooserCounts(clonedTarget);
- finish();
+ if (!mAwaitingDelegateResponse) {
+ finish();
+ }
return;
}
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 07c4050..140fabc 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -299,18 +299,19 @@
// Consolidate multiple targets from same app.
Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
for (DisplayResolveInfo info : allTargets) {
- String packageName = info.getResolvedComponentName().getPackageName();
- DisplayResolveInfo multiDri = consolidated.get(packageName);
+ String resolvedTarget = info.getResolvedComponentName().getPackageName()
+ + '#' + info.getDisplayLabel();
+ DisplayResolveInfo multiDri = consolidated.get(resolvedTarget);
if (multiDri == null) {
- consolidated.put(packageName, info);
+ consolidated.put(resolvedTarget, info);
} else if (multiDri instanceof MultiDisplayResolveInfo) {
((MultiDisplayResolveInfo) multiDri).addTarget(info);
} else {
// create consolidated target from the single DisplayResolveInfo
MultiDisplayResolveInfo multiDisplayResolveInfo =
- new MultiDisplayResolveInfo(packageName, multiDri);
+ new MultiDisplayResolveInfo(resolvedTarget, multiDri);
multiDisplayResolveInfo.addTarget(info);
- consolidated.put(packageName, multiDisplayResolveInfo);
+ consolidated.put(resolvedTarget, multiDisplayResolveInfo);
}
}
List<DisplayResolveInfo> groupedTargets = new ArrayList<>();
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index d08f21c..85d27a1 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -152,6 +152,8 @@
/** See {@link #setRetainInOnStop}. */
private boolean mRetainInOnStop;
+ private static final int REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER = 20;
+
private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
@@ -203,6 +205,8 @@
private UserHandle mWorkProfileUserHandle;
+ protected boolean mAwaitingDelegateResponse;
+
/**
* Get the string resource to be used as a label for the link to the resolver activity for an
* action.
@@ -584,7 +588,9 @@
mProfileSwitchMessageId = -1;
onTargetSelected(dri, false);
- finish();
+ if (!mAwaitingDelegateResponse) {
+ finish();
+ }
}
/**
@@ -1026,7 +1032,9 @@
mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
: MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
- finish();
+ if (!mAwaitingDelegateResponse) {
+ finish();
+ }
}
}
@@ -1354,15 +1362,31 @@
chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY,
ignoreTargetSecurity);
chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
- chooserIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
- | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
- startActivity(chooserIntent);
+ chooserIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+
+ // Don't close until the delegate finishes, or the token will be invalidated.
+ mAwaitingDelegateResponse = true;
+
+ startActivityForResult(chooserIntent, REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
return true;
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER:
+ // Repeat the delegate's result as our own.
+ setResult(resultCode, data);
+ finish();
+ break;
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
public void onActivityStarted(TargetInfo cti) {
// Do nothing
}
@@ -2152,7 +2176,9 @@
.getItem(selections[0].getIndex());
if (ra.onTargetSelected(ti, false)) {
ra.mPickOptionRequest = null;
- ra.finish();
+ if (!ra.mAwaitingDelegateResponse) {
+ ra.finish();
+ }
}
}
}
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index ce75f45..068b882 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -165,7 +165,7 @@
// Now fetch app icon and raster with no badging even in work profile
Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info)
- .getIconBitmap(android.os.Process.myUserHandle());
+ .getIconBitmap(mContext.getUser());
// Raster target drawable with appIcon as a badge
SimpleIconFactory sif = SimpleIconFactory.obtain(mContext);
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index 0cbdc13..2fde981 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -477,15 +477,16 @@
* Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
*
* @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
+ * @param imeDisplayId the display ID that is associated with the IME.
* @return {@link AndroidFuture<Boolean>} that can be used to retrieve the invocation
* result. {@link RemoteException} will be treated as an error.
*/
@AnyThread
@NonNull
- public AndroidFuture<Boolean> requestCursorUpdates(int cursorUpdateMode) {
+ public AndroidFuture<Boolean> requestCursorUpdates(int cursorUpdateMode, int imeDisplayId) {
final AndroidFuture<Boolean> future = new AndroidFuture<>();
try {
- mIInputContext.requestCursorUpdates(cursorUpdateMode, future);
+ mIInputContext.requestCursorUpdates(cursorUpdateMode, imeDisplayId, future);
} catch (RemoteException e) {
future.completeExceptionally(e);
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 662bd28..29c1b1b 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -16,6 +16,13 @@
package com.android.internal.inputmethod;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetCursorCapsModeProto;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetExtractedTextProto;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetSelectedTextProto;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetSurroundingTextProto;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetTextAfterCursorProto;
+import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildGetTextBeforeCursorProto;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -29,20 +36,17 @@
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DumpableInputConnection;
-import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionInspector;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.SurroundingText;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
import java.lang.ref.WeakReference;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -139,10 +143,10 @@
if (ic == null) {
return;
}
- @MissingMethodFlags
- final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
- if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
+ try {
ic.closeConnection();
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
}
} finally {
synchronized (mLock) {
@@ -225,21 +229,12 @@
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextAfterCursor", future, () -> {
final InputConnection ic = getInputConnection();
- final CharSequence result;
if (ic == null || !isActive()) {
Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
- result = null;
- } else {
- result = ic.getTextAfterCursor(length, flags);
+ return null;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(
- length, flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
- }
- return result;
- });
+ return ic.getTextAfterCursor(length, flags);
+ }, useImeTracing() ? result -> buildGetTextAfterCursorProto(length, flags, result) : null);
}
@Override
@@ -247,42 +242,29 @@
AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getTextBeforeCursor", future, () -> {
final InputConnection ic = getInputConnection();
- final CharSequence result;
if (ic == null || !isActive()) {
Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
- result = null;
- } else {
- result = ic.getTextBeforeCursor(length, flags);
+ return null;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(
- length, flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
- }
- return result;
- });
+ return ic.getTextBeforeCursor(length, flags);
+ }, useImeTracing() ? result -> buildGetTextBeforeCursorProto(length, flags, result) : null);
}
@Override
public void getSelectedText(int flags, AndroidFuture future /* T=CharSequence */) {
dispatchWithTracing("getSelectedText", future, () -> {
final InputConnection ic = getInputConnection();
- final CharSequence result;
if (ic == null || !isActive()) {
Log.w(TAG, "getSelectedText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getSelectedText(flags);
+ return null;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(
- flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSelectedText", mParentInputMethodManager, icProto);
+ try {
+ return ic.getSelectedText(flags);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ return null;
}
- return result;
- });
+ }, useImeTracing() ? result -> buildGetSelectedTextProto(flags, result) : null);
}
@Override
@@ -290,42 +272,25 @@
AndroidFuture future /* T=SurroundingText */) {
dispatchWithTracing("getSurroundingText", future, () -> {
final InputConnection ic = getInputConnection();
- final SurroundingText result;
if (ic == null || !isActive()) {
Log.w(TAG, "getSurroundingText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getSurroundingText(beforeLength, afterLength, flags);
+ return null;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
- beforeLength, afterLength, flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
- }
- return result;
- });
+ return ic.getSurroundingText(beforeLength, afterLength, flags);
+ }, useImeTracing() ? result -> buildGetSurroundingTextProto(
+ beforeLength, afterLength, flags, result) : null);
}
@Override
public void getCursorCapsMode(int reqModes, AndroidFuture future /* T=Integer */) {
dispatchWithTracing("getCursorCapsMode", future, () -> {
final InputConnection ic = getInputConnection();
- final int result;
if (ic == null || !isActive()) {
Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
- result = 0;
- } else {
- result = ic.getCursorCapsMode(reqModes);
+ return 0;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
- reqModes, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
- }
- return result;
- });
+ return ic.getCursorCapsMode(reqModes);
+ }, useImeTracing() ? result -> buildGetCursorCapsModeProto(reqModes, result) : null);
}
@Override
@@ -333,21 +298,12 @@
AndroidFuture future /* T=ExtractedText */) {
dispatchWithTracing("getExtractedText", future, () -> {
final InputConnection ic = getInputConnection();
- final ExtractedText result;
if (ic == null || !isActive()) {
Log.w(TAG, "getExtractedText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getExtractedText(request, flags);
+ return null;
}
- if (ImeTracing.getInstance().isEnabled()) {
- final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
- request, flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getExtractedText", mParentInputMethodManager, icProto);
- }
- return result;
- });
+ return ic.getExtractedText(request, flags);
+ }, useImeTracing() ? result -> buildGetExtractedTextProto(request, flags, result) : null);
}
@Override
@@ -382,7 +338,11 @@
Log.w(TAG, "commitCorrection on inactive InputConnection");
return;
}
- ic.commitCorrection(info);
+ try {
+ ic.commitCorrection(info);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ }
});
}
@@ -430,7 +390,11 @@
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
}
- ic.setComposingRegion(start, end);
+ try {
+ ic.setComposingRegion(start, end);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ }
});
}
@@ -514,7 +478,11 @@
Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
return;
}
- ic.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ try {
+ ic.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ }
});
}
@@ -567,14 +535,24 @@
}
@Override
- public void requestCursorUpdates(int cursorUpdateMode, AndroidFuture future /* T=Boolean */) {
+ public void requestCursorUpdates(int cursorUpdateMode, int imeDisplayId,
+ AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
final InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
return false;
}
- return ic.requestCursorUpdates(cursorUpdateMode);
+ if (mParentInputMethodManager.getDisplayId() != imeDisplayId) {
+ // requestCursorUpdates() is not currently supported across displays.
+ return false;
+ }
+ try {
+ return ic.requestCursorUpdates(cursorUpdateMode);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ return false;
+ }
});
}
@@ -591,7 +569,12 @@
Log.w(TAG, "commitContent with invalid inputContentInfo=" + inputContentInfo);
return false;
}
- return ic.commitContent(inputContentInfo, flags, opts);
+ try {
+ return ic.commitContent(inputContentInfo, flags, opts);
+ } catch (AbstractMethodError ignored) {
+ // TODO(b/199934664): See if we can remove this by providing a default impl.
+ return false;
+ }
});
}
@@ -638,6 +621,12 @@
private <T> void dispatchWithTracing(@NonNull String methodName,
@NonNull AndroidFuture untypedFuture, @NonNull Supplier<T> supplier) {
+ dispatchWithTracing(methodName, untypedFuture, supplier, null /* dumpProtoProvider */);
+ }
+
+ private <T> void dispatchWithTracing(@NonNull String methodName,
+ @NonNull AndroidFuture untypedFuture, @NonNull Supplier<T> supplier,
+ @Nullable Function<T, byte[]> dumpProtoProvider) {
@SuppressWarnings("unchecked")
final AndroidFuture<T> future = untypedFuture;
dispatchWithTracing(methodName, () -> {
@@ -649,6 +638,15 @@
throw throwable;
}
future.complete(result);
+ if (dumpProtoProvider != null) {
+ final byte[] icProto = dumpProtoProvider.apply(result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#" + methodName, mParentInputMethodManager, icProto);
+ }
});
}
+
+ private static boolean useImeTracing() {
+ return ImeTracing.getInstance().isEnabled();
+ }
}
diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
index 4ce6f60..3eb9804 100644
--- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
+++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
@@ -17,19 +17,27 @@
package com.android.internal.notification;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import com.android.internal.R;
+
+/**
+ * This class provides methods to create intents for NotificationAccessConfirmationActivity.
+ */
public final class NotificationAccessConfirmationActivityContract {
- private static final ComponentName COMPONENT_NAME = new ComponentName(
- "com.android.settings",
- "com.android.settings.notification.NotificationAccessConfirmationActivity");
public static final String EXTRA_USER_ID = "user_id";
public static final String EXTRA_COMPONENT_NAME = "component_name";
public static final String EXTRA_PACKAGE_TITLE = "package_title";
- public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) {
+ /**
+ * Creates a launcher intent for NotificationAccessConfirmationActivity.
+ */
+ public static Intent launcherIntent(Context context, int userId, ComponentName component,
+ String packageTitle) {
return new Intent()
- .setComponent(COMPONENT_NAME)
+ .setComponent(ComponentName.unflattenFromString(context.getString(
+ R.string.config_notificationAccessConfirmationActivity)))
.putExtra(EXTRA_USER_ID, userId)
.putExtra(EXTRA_COMPONENT_NAME, component)
.putExtra(EXTRA_PACKAGE_TITLE, packageTitle);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 719dc53..7c4de82 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8578,7 +8578,7 @@
* inactive so can be dropped.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean reset(long uptimeUs, long realtimeUs) {
+ public boolean reset(long uptimeUs, long realtimeUs, int resetReason) {
boolean active = false;
mOnBatteryBackgroundTimeBase.init(uptimeUs, realtimeUs);
@@ -8648,7 +8648,11 @@
resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
resetIfNotNull(mModemControllerActivity, false, realtimeUs);
- MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+ if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
+ mUidMeasuredEnergyStats = null;
+ } else {
+ MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+ }
resetIfNotNull(mUserCpuTime, false, realtimeUs);
resetIfNotNull(mSystemCpuTime, false, realtimeUs);
@@ -11332,7 +11336,7 @@
mNumConnectivityChange = 0;
for (int i=0; i<mUidStats.size(); i++) {
- if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs)) {
+ if (mUidStats.valueAt(i).reset(uptimeUs, elapsedRealtimeUs, resetReason)) {
mUidStats.valueAt(i).detachFromTimeBase();
mUidStats.remove(mUidStats.keyAt(i));
i--;
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 9b51a8e..bb307a0 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -21,13 +21,18 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.util.Slog;
import android.util.SparseArray;
+import java.util.Arrays;
+
/**
* Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
* {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
*/
public class CustomMeasuredPowerCalculator extends PowerCalculator {
+ private static final String TAG = "CustomMeasuredPowerCalc";
+
public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
}
@@ -76,9 +81,9 @@
if (totalPowerMah == null) {
newTotalPowerMah = new double[customMeasuredPowerMah.length];
} else if (totalPowerMah.length != customMeasuredPowerMah.length) {
- newTotalPowerMah = new double[customMeasuredPowerMah.length];
- System.arraycopy(totalPowerMah, 0, newTotalPowerMah, 0,
- customMeasuredPowerMah.length);
+ Slog.wtf(TAG, "Number of custom energy components is not the same for all apps: "
+ + totalPowerMah.length + ", " + customMeasuredPowerMah.length);
+ newTotalPowerMah = Arrays.copyOf(totalPowerMah, customMeasuredPowerMah.length);
} else {
newTotalPowerMah = totalPowerMah;
}
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index fc0ce6f..31952eb 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -24,6 +24,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import dalvik.annotation.optimization.CriticalNative;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -248,6 +250,32 @@
}
public native long[] readBpfData(int uid);
+
+ /**
+ * Reads CPU time-in-state data for the specified UID and adds the delta since the
+ * previous call to the current state stats in the LongArrayMultiStateCounter.
+ */
+ public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) {
+ return addDeltaFromBpf(uid, counter.mNativeObject, timestampMs);
+ }
+
+ @CriticalNative
+ private static native boolean addDeltaFromBpf(int uid,
+ long longArrayMultiStateCounterNativePointer, long timestampMs);
+
+ /**
+ * Used for testing.
+ *
+ * Takes mock cpu-time-in-frequency data and uses it the same way eBPF data would be used.
+ */
+ public boolean addDeltaForTest(int uid, LongArrayMultiStateCounter counter,
+ long timestampMs, long[][] timeInFreqDataNanos) {
+ return addDeltaForTest(uid, counter.mNativeObject, timestampMs, timeInFreqDataNanos);
+ }
+
+ private static native boolean addDeltaForTest(int uid,
+ long longArrayMultiStateCounterNativePointer, long timestampMs,
+ long[][] timeInFreqDataNanos);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 1a3b29d..aecab3d 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -16,6 +16,11 @@
package com.android.internal.os;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -31,7 +36,7 @@
* // At 1000 ms, the state changes to 1
* counter.setState(1, 1000);
*
- * // At 3000 ms, the tracked values are updated to {100, 200}
+ * // At 3000 ms, the tracked values are updated to {30, 300}
* arrayContainer.setValues(new long[]{{30, 300}};
* counter.updateValues(arrayContainer, 3000);
*
@@ -47,7 +52,7 @@
*
* @hide
*/
-public class LongArrayMultiStateCounter {
+public final class LongArrayMultiStateCounter implements Parcelable {
/**
* Container for a native equivalent of a long[].
@@ -107,16 +112,35 @@
private final int mStateCount;
private final int mLength;
- private final long mNativeObject;
- public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
- long timestampMs) {
+ // Visible to other objects in this package so that it can be passed to @CriticalNative
+ // methods.
+ final long mNativeObject;
+
+ public LongArrayMultiStateCounter(int stateCount, int arrayLength) {
+ Preconditions.checkArgumentPositive(stateCount, "stateCount must be greater than 0");
mStateCount = stateCount;
mLength = arrayLength;
- mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs);
+ mNativeObject = native_init(stateCount, arrayLength);
sRegistry.registerNativeAllocation(this, mNativeObject);
}
+ private LongArrayMultiStateCounter(Parcel in) {
+ mNativeObject = native_initFromParcel(in);
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+
+ mStateCount = native_getStateCount(mNativeObject);
+ mLength = native_getArrayLength(mNativeObject);
+ }
+
+ /**
+ * Enables or disables the counter. When the counter is disabled, it does not
+ * accumulate counts supplied by the {@link #updateValues} method.
+ */
+ public void setEnabled(boolean enabled, long timestampMs) {
+ native_setEnabled(mNativeObject, enabled, timestampMs);
+ }
+
/**
* Sets the current state to the supplied value.
*/
@@ -143,6 +167,13 @@
}
/**
+ * Resets the accumulated counts to 0.
+ */
+ public void reset() {
+ native_reset(mNativeObject);
+ }
+
+ /**
* Populates longArrayContainer with the accumulated counts for the specified state.
*/
public void getCounts(LongArrayContainer longArrayContainer, int state) {
@@ -158,14 +189,41 @@
return native_toString(mNativeObject);
}
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ native_writeToParcel(mNativeObject, dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<LongArrayMultiStateCounter> CREATOR =
+ new Creator<LongArrayMultiStateCounter>() {
+ @Override
+ public LongArrayMultiStateCounter createFromParcel(Parcel in) {
+ return new LongArrayMultiStateCounter(in);
+ }
+
+ @Override
+ public LongArrayMultiStateCounter[] newArray(int size) {
+ return new LongArrayMultiStateCounter[size];
+ }
+ };
+
+
@CriticalNative
- private static native long native_init(int stateCount, int arrayLength, int initialState,
- long timestampMs);
+ private static native long native_init(int stateCount, int arrayLength);
@CriticalNative
private static native long native_getReleaseFunc();
@CriticalNative
+ private static native void native_setEnabled(long nativeObject, boolean enabled,
+ long timestampMs);
+
+ @CriticalNative
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
@@ -173,9 +231,24 @@
long longArrayContainerNativeObject, long timestampMs);
@CriticalNative
+ private static native void native_reset(long nativeObject);
+
+ @CriticalNative
private static native void native_getCounts(long nativeObject,
long longArrayContainerNativeObject, int state);
@FastNative
private native String native_toString(long nativeObject);
+
+ @FastNative
+ private native void native_writeToParcel(long nativeObject, Parcel dest, int flags);
+
+ @FastNative
+ private static native long native_initFromParcel(Parcel parcel);
+
+ @CriticalNative
+ private static native int native_getStateCount(long nativeObject);
+
+ @CriticalNative
+ private static native int native_getArrayLength(long nativeObject);
}
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index b321ac0..a09c823 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -124,7 +124,6 @@
Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- sendCloseSystemWindows();
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
startCallActivity();
@@ -147,7 +146,6 @@
dispatcher.performedLongPress(event);
if (isUserSetupComplete()) {
mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
// Broadcast an intent that the Camera button was longpressed
Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -178,7 +176,6 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
getSearchManager().stopSearch();
mContext.startActivity(intent);
// Only clear this if we successfully start the
@@ -272,7 +269,6 @@
@UnsupportedAppUsage
void startCallActivity() {
- sendCloseSystemWindows();
Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
@@ -319,10 +315,6 @@
return mMediaSessionManager;
}
- void sendCloseSystemWindows() {
- PhoneWindow.sendCloseSystemWindows(mContext, null);
- }
-
private void handleVolumeKeyEvent(KeyEvent keyEvent) {
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent,
AudioManager.USE_DEFAULT_STREAM_TYPE);
diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java
index 9df4bdb..f0ca1edb 100644
--- a/core/java/com/android/internal/util/BinaryXmlSerializer.java
+++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java
@@ -124,7 +124,7 @@
throw new UnsupportedOperationException();
}
- mOut = new FastDataOutput(os, BUFFER_SIZE);
+ mOut = FastDataOutput.obtain(os);
mOut.write(PROTOCOL_MAGIC_VERSION_0);
mTagCount = 0;
@@ -138,7 +138,9 @@
@Override
public void flush() throws IOException {
- mOut.flush();
+ if (mOut != null) {
+ mOut.flush();
+ }
}
@Override
@@ -157,6 +159,9 @@
public void endDocument() throws IOException {
mOut.writeByte(END_DOCUMENT | TYPE_NULL);
flush();
+
+ mOut.release();
+ mOut = null;
}
@Override
diff --git a/core/java/com/android/internal/util/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java
index cf5b296..bc8496b 100644
--- a/core/java/com/android/internal/util/FastDataOutput.java
+++ b/core/java/com/android/internal/util/FastDataOutput.java
@@ -30,6 +30,7 @@
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Optimized implementation of {@link DataOutput} which buffers data in memory
@@ -41,23 +42,26 @@
public class FastDataOutput implements DataOutput, Flushable, Closeable {
private static final int MAX_UNSIGNED_SHORT = 65_535;
+ private static final int BUFFER_SIZE = 32_768;
+
+ private static AtomicReference<FastDataOutput> sOutCache = new AtomicReference<>();
+
private final VMRuntime mRuntime;
- private final OutputStream mOut;
private final byte[] mBuffer;
private final long mBufferPtr;
private final int mBufferCap;
+ private OutputStream mOut;
private int mBufferPos;
/**
* Values that have been "interned" by {@link #writeInternedUTF(String)}.
*/
- private HashMap<String, Short> mStringRefs = new HashMap<>();
+ private final HashMap<String, Short> mStringRefs = new HashMap<>();
public FastDataOutput(@NonNull OutputStream out, int bufferSize) {
mRuntime = VMRuntime.getRuntime();
- mOut = Objects.requireNonNull(out);
if (bufferSize < 8) {
throw new IllegalArgumentException();
}
@@ -65,6 +69,48 @@
mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
+
+ setOutput(out);
+ }
+
+ /**
+ * Create a new FastDataOutput object or retrieve one from cache.
+ */
+ public static FastDataOutput obtain(@NonNull OutputStream out) {
+ FastDataOutput instance = sOutCache.getAndSet(null);
+ if (instance != null) {
+ instance.setOutput(out);
+ return instance;
+ }
+ return new FastDataOutput(out, BUFFER_SIZE);
+ }
+
+ /**
+ * Put a FastDataOutput object back into the cache.
+ * You must not touch the object after this call.
+ */
+ public void release() {
+ if (mBufferPos > 0) {
+ throw new IllegalStateException("Lingering data, call flush() before releasing.");
+ }
+
+ mOut = null;
+ mBufferPos = 0;
+ mStringRefs.clear();
+
+ if (mBufferCap == BUFFER_SIZE) {
+ // Try to return to the cache.
+ sOutCache.compareAndSet(null, this);
+ }
+ }
+
+ /**
+ * Re-initializes the object for the new output.
+ */
+ private void setOutput(@NonNull OutputStream out) {
+ mOut = Objects.requireNonNull(out);
+ mBufferPos = 0;
+ mStringRefs.clear();
}
private void drain() throws IOException {
@@ -83,6 +129,7 @@
@Override
public void close() throws IOException {
mOut.close();
+ release();
}
@Override
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index f040462..5144a91 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,15 +14,20 @@
package com.android.internal.util;
+import static android.os.Trace.TRACE_TAG_APP;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import android.util.SparseLongArray;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.EventLogTags;
@@ -31,6 +36,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
/**
* Class to track various latencies in SystemUI. It then writes the latency to statsd and also
@@ -44,6 +50,7 @@
private static final String TAG = "LatencyTracker";
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final boolean DEBUG = false;
/** Default to being enabled on debug builds. */
private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
/** Default to collecting data for 1/5 of all actions (randomly sampled). */
@@ -162,7 +169,8 @@
private static LatencyTracker sLatencyTracker;
private final Object mLock = new Object();
- private final SparseLongArray mStartRtc = new SparseLongArray();
+ @GuardedBy("mLock")
+ private final SparseArray<Session> mSessions = new SparseArray<>();
@GuardedBy("mLock")
private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
@GuardedBy("mLock")
@@ -244,8 +252,12 @@
}
}
- private static String getTraceNameOfAction(@Action int action) {
- return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
+ private static String getTraceNameOfAction(@Action int action, String tag) {
+ if (TextUtils.isEmpty(tag)) {
+ return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
+ } else {
+ return "L<" + getNameOfAction(STATSD_ACTION[action]) + "::" + tag + ">";
+ }
}
private static String getTraceTriggerNameForAction(@Action int action) {
@@ -263,35 +275,82 @@
}
/**
- * Notifies that an action is starting. This needs to be called from the main thread.
+ * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
*
* @param action The action to start. One of the ACTION_* values.
*/
public void onActionStart(@Action int action) {
- if (!isEnabled()) {
- return;
- }
- Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
- mStartRtc.put(action, SystemClock.elapsedRealtime());
+ onActionStart(action, null);
}
/**
- * Notifies that an action has ended. This needs to be called from the main thread.
+ * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
+ *
+ * @param action The action to start. One of the ACTION_* values.
+ * @param tag The brief description of the action.
+ */
+ public void onActionStart(@Action int action, String tag) {
+ synchronized (mLock) {
+ if (!isEnabled()) {
+ return;
+ }
+ // skip if the action is already instrumenting.
+ if (mSessions.get(action) != null) {
+ return;
+ }
+ Session session = new Session(action, tag);
+ session.begin(() -> onActionCancel(action));
+ mSessions.put(action, session);
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionStart: " + session.name() + ", start=" + session.mStartRtc);
+ }
+ }
+ }
+
+ /**
+ * Notifies that an action has ended. <s>This needs to be called from the main thread.</s>
*
* @param action The action to end. One of the ACTION_* values.
*/
public void onActionEnd(@Action int action) {
- if (!isEnabled()) {
- return;
+ synchronized (mLock) {
+ if (!isEnabled()) {
+ return;
+ }
+ Session session = mSessions.get(action);
+ if (session == null) {
+ return;
+ }
+ session.end();
+ mSessions.delete(action);
+ logAction(action, session.duration());
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionEnd:" + session.name() + ", duration=" + session.duration());
+ }
}
- long endRtc = SystemClock.elapsedRealtime();
- long startRtc = mStartRtc.get(action, -1);
- if (startRtc == -1) {
- return;
+ }
+
+ /**
+ * Notifies that an action has canceled. <s>This needs to be called from the main thread.</s>
+ *
+ * @param action The action to cancel. One of the ACTION_* values.
+ * @hide
+ */
+ public void onActionCancel(@Action int action) {
+ synchronized (mLock) {
+ Session session = mSessions.get(action);
+ if (session == null) {
+ return;
+ }
+ session.cancel();
+ mSessions.delete(action);
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionCancel: " + session.name());
+ }
}
- mStartRtc.delete(action);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
- logAction(action, (int) (endRtc - startRtc));
}
/**
@@ -332,4 +391,57 @@
FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
}
}
+
+ static class Session {
+ @Action
+ private final int mAction;
+ private final String mTag;
+ private final String mName;
+ private Runnable mTimeoutRunnable;
+ private long mStartRtc = -1;
+ private long mEndRtc = -1;
+
+ Session(@Action int action, @Nullable String tag) {
+ mAction = action;
+ mTag = tag;
+ mName = TextUtils.isEmpty(mTag)
+ ? getNameOfAction(STATSD_ACTION[mAction])
+ : getNameOfAction(STATSD_ACTION[mAction]) + "::" + mTag;
+ }
+
+ String name() {
+ return mName;
+ }
+
+ String traceName() {
+ return getTraceNameOfAction(mAction, mTag);
+ }
+
+ void begin(@NonNull Runnable timeoutAction) {
+ mStartRtc = SystemClock.elapsedRealtime();
+ Trace.asyncTraceBegin(TRACE_TAG_APP, traceName(), 0);
+
+ // start counting timeout.
+ mTimeoutRunnable = timeoutAction;
+ BackgroundThread.getHandler()
+ .postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(2));
+ }
+
+ void end() {
+ mEndRtc = SystemClock.elapsedRealtime();
+ Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0);
+ BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+
+ void cancel() {
+ Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0);
+ BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+
+ int duration() {
+ return (int) (mEndRtc - mStartRtc);
+ }
+ }
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 12a98c1..2e6f9e5 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -75,7 +75,8 @@
void getSelectedText(int flags, in AndroidFuture future /* T=CharSequence */);
- void requestCursorUpdates(int cursorUpdateMode, in AndroidFuture future /* T=Boolean */);
+ void requestCursorUpdates(int cursorUpdateMode, int imeDisplayId,
+ in AndroidFuture future /* T=Boolean */);
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
in AndroidFuture future /* T=Boolean */);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 5354afb..139660a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -45,7 +45,7 @@
void unbindInput();
- void startInput(in IBinder startInputToken, in IInputContext inputContext, int missingMethods,
+ void startInput(in IBinder startInputToken, in IInputContext inputContext,
in EditorInfo attribute, boolean restarting);
void createSession(in InputChannel channel, IInputSessionCallback callback);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4b72355..350ec33 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -54,7 +54,6 @@
/* @StartInputFlags */ int startInputFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
int unverifiedTargetSdkVersion);
void showInputMethodPickerFromClient(in IInputMethodClient client,
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 8d63a40..2f25d60 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -21,10 +21,12 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.HardwareRenderer;
+import android.graphics.HardwareRenderer.SyncAndDrawResult;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.os.CancellationSignal;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -52,7 +54,7 @@
@UiThread
public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
- private static final String TAG = "ScrollCaptureViewSupport";
+ private static final String TAG = "SCViewSupport";
private static final String SETTING_CAPTURE_DELAY = "screenshot.scroll_capture_delay";
private static final long SETTING_CAPTURE_DELAY_DEFAULT = 60L; // millis
@@ -264,8 +266,25 @@
if (signal.isCanceled()) {
Log.w(TAG, "onScrollCaptureImageRequest: cancelled! skipping render.");
} else {
- mRenderer.renderView(view, viewCaptureArea);
- onComplete.accept(new Rect(scrollResult.availableArea));
+ int result = mRenderer.renderView(view, viewCaptureArea);
+ switch (result) {
+ case HardwareRenderer.SYNC_OK:
+ case HardwareRenderer.SYNC_REDRAW_REQUESTED:
+ /* Frame synced, buffer will be produced... notify client. */
+ onComplete.accept(new Rect(scrollResult.availableArea));
+ return;
+ case HardwareRenderer.SYNC_FRAME_DROPPED:
+ Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
+ break;
+ case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
+ Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
+ break;
+ case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
+ Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
+ break;
+ }
+ // No buffer will be produced.
+ onComplete.accept(new Rect(/* empty */));
}
};
@@ -373,37 +392,16 @@
mCaptureRenderNode.endRecording();
}
- public void renderView(View view, Rect sourceRect) {
+ @SyncAndDrawResult
+ public int renderView(View view, Rect sourceRect) {
+ HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
+ request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
if (updateForView(view)) {
setupLighting(view);
}
view.invalidate();
updateRootNode(view, sourceRect);
- HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
- long timestamp = System.nanoTime();
- request.setVsyncTime(timestamp);
-
- // Would be nice to access nextFrameNumber from HwR without having to hold on to Surface
- final long frameNumber = mSurface.getNextFrameNumber();
-
- // Block until a frame is presented to the Surface
- request.setWaitForPresent(true);
-
- switch (request.syncAndDraw()) {
- case HardwareRenderer.SYNC_OK:
- case HardwareRenderer.SYNC_REDRAW_REQUESTED:
- return;
-
- case HardwareRenderer.SYNC_FRAME_DROPPED:
- Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
- break;
- case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
- Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
- break;
- case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
- Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
- break;
- }
+ return request.syncAndDraw();
}
public void trimMemory() {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index dbf4528..5a03277 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
@@ -1264,6 +1265,14 @@
}
/**
+ * Whether the user is not allowed to set any credentials via PASSWORD_QUALITY_MANAGED.
+ */
+ public boolean isCredentialsDisabledForUser(int userId) {
+ return getDevicePolicyManager().getPasswordQuality(/* admin= */ null, userId)
+ == PASSWORD_QUALITY_MANAGED;
+ }
+
+ /**
* @see StrongAuthTracker#isTrustAllowedForUser
*/
public boolean isTrustAllowedForUser(int userId) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 21a327c..945adb7 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -263,6 +263,7 @@
"libdebuggerd_client",
"libutils",
"libbinder",
+ "libbinder_ndk",
"libui",
"libgraphicsenv",
"libgui",
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index b4b727b..db33863d 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -920,26 +920,6 @@
return true;
}
-static status_t moveEntries(sp<TiffWriter> writer, uint32_t ifdFrom, uint32_t ifdTo,
- const Vector<uint16_t>& entries) {
- for (size_t i = 0; i < entries.size(); ++i) {
- uint16_t tagId = entries[i];
- sp<TiffEntry> entry = writer->getEntry(tagId, ifdFrom);
- if (entry.get() == nullptr) {
- ALOGE("%s: moveEntries failed, entry %u not found in IFD %u", __FUNCTION__, tagId,
- ifdFrom);
- return BAD_VALUE;
- }
- if (writer->addEntry(entry, ifdTo) != OK) {
- ALOGE("%s: moveEntries failed, could not add entry %u to IFD %u", __FUNCTION__, tagId,
- ifdFrom);
- return BAD_VALUE;
- }
- writer->removeEntry(tagId, ifdFrom);
- }
- return OK;
-}
-
/**
* Write CFA pattern for given CFA enum into cfaOut. cfaOut must have length >= 4.
* Returns OK on success, or a negative error code if the CFA enum was invalid.
@@ -2255,66 +2235,27 @@
}
}
- Vector<uint16_t> tagsToMove;
- tagsToMove.add(TAG_NEWSUBFILETYPE);
- tagsToMove.add(TAG_ACTIVEAREA);
- tagsToMove.add(TAG_BITSPERSAMPLE);
- tagsToMove.add(TAG_COMPRESSION);
- tagsToMove.add(TAG_IMAGEWIDTH);
- tagsToMove.add(TAG_IMAGELENGTH);
- tagsToMove.add(TAG_PHOTOMETRICINTERPRETATION);
- tagsToMove.add(TAG_BLACKLEVEL);
- tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
- tagsToMove.add(TAG_SAMPLESPERPIXEL);
- tagsToMove.add(TAG_PLANARCONFIGURATION);
- if (isBayer) {
- tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
- tagsToMove.add(TAG_CFAPATTERN);
- tagsToMove.add(TAG_CFAPLANECOLOR);
- tagsToMove.add(TAG_CFALAYOUT);
- }
- tagsToMove.add(TAG_XRESOLUTION);
- tagsToMove.add(TAG_YRESOLUTION);
- tagsToMove.add(TAG_RESOLUTIONUNIT);
- tagsToMove.add(TAG_WHITELEVEL);
- tagsToMove.add(TAG_DEFAULTSCALE);
- tagsToMove.add(TAG_DEFAULTCROPORIGIN);
- tagsToMove.add(TAG_DEFAULTCROPSIZE);
-
- if (nullptr != writer->getEntry(TAG_OPCODELIST2, TIFF_IFD_0).get()) {
- tagsToMove.add(TAG_OPCODELIST2);
- }
-
- if (nullptr != writer->getEntry(TAG_OPCODELIST3, TIFF_IFD_0).get()) {
- tagsToMove.add(TAG_OPCODELIST3);
- }
-
- if (moveEntries(writer, TIFF_IFD_0, TIFF_IFD_SUB1, tagsToMove) != OK) {
- jniThrowException(env, "java/lang/IllegalStateException", "Failed to move entries");
- return nullptr;
- }
-
// Setup thumbnail tags
{
// Set photometric interpretation
uint16_t interpretation = 2; // RGB
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
- &interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
+ &interpretation, TIFF_IFD_SUB1), env, TAG_PHOTOMETRICINTERPRETATION, writer);
}
{
// Set planar configuration
uint16_t config = 1; // Chunky
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PLANARCONFIGURATION, 1, &config,
- TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
+ TIFF_IFD_SUB1), env, TAG_PLANARCONFIGURATION, writer);
}
{
// Set samples per pixel
uint16_t samples = SAMPLES_PER_RGB_PIXEL;
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_SAMPLESPERPIXEL, 1, &samples,
- TIFF_IFD_0), env, TAG_SAMPLESPERPIXEL, writer);
+ TIFF_IFD_SUB1), env, TAG_SAMPLESPERPIXEL, writer);
}
{
@@ -2322,7 +2263,7 @@
uint16_t bits[SAMPLES_PER_RGB_PIXEL];
for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
BAIL_IF_INVALID_RET_NULL_SP(
- writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
+ writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_SUB1),
env, TAG_BITSPERSAMPLE, writer);
}
@@ -2330,55 +2271,55 @@
// Set subfiletype
uint32_t subfileType = 1; // Thumbnail image
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_NEWSUBFILETYPE, 1, &subfileType,
- TIFF_IFD_0), env, TAG_NEWSUBFILETYPE, writer);
+ TIFF_IFD_SUB1), env, TAG_NEWSUBFILETYPE, writer);
}
{
// Set compression
uint16_t compression = 1; // None
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_COMPRESSION, 1, &compression,
- TIFF_IFD_0), env, TAG_COMPRESSION, writer);
+ TIFF_IFD_SUB1), env, TAG_COMPRESSION, writer);
}
{
// Set dimensions
uint32_t uWidth = nativeContext->getThumbnailWidth();
uint32_t uHeight = nativeContext->getThumbnailHeight();
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_0),
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGEWIDTH, 1, &uWidth, TIFF_IFD_SUB1),
env, TAG_IMAGEWIDTH, writer);
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight, TIFF_IFD_0),
- env, TAG_IMAGELENGTH, writer);
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_IMAGELENGTH, 1, &uHeight,
+ TIFF_IFD_SUB1), env, TAG_IMAGELENGTH, writer);
}
{
// x resolution
uint32_t xres[] = { 72, 1 }; // default 72 ppi
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_SUB1),
env, TAG_XRESOLUTION, writer);
// y resolution
uint32_t yres[] = { 72, 1 }; // default 72 ppi
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_SUB1),
env, TAG_YRESOLUTION, writer);
uint16_t unit = 2; // inches
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
- env, TAG_RESOLUTIONUNIT, writer);
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit,
+ TIFF_IFD_SUB1), env, TAG_RESOLUTIONUNIT, writer);
}
}
if (writer->addStrip(TIFF_IFD_0) != OK) {
- ALOGE("%s: Could not setup thumbnail strip tags.", __FUNCTION__);
+ ALOGE("%s: Could not setup main image strip tags.", __FUNCTION__);
jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to setup thumbnail strip tags.");
+ "Failed to setup main image strip tags.");
return nullptr;
}
if (writer->hasIfd(TIFF_IFD_SUB1)) {
if (writer->addStrip(TIFF_IFD_SUB1) != OK) {
- ALOGE("%s: Could not main image strip tags.", __FUNCTION__);
+ ALOGE("%s: Could not thumbnail image strip tags.", __FUNCTION__);
jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to setup main image strip tags.");
+ "Failed to setup thumbnail image strip tags.");
return nullptr;
}
}
@@ -2548,19 +2489,15 @@
Vector<StripSource*> sources;
sp<DirectStripSource> thumbnailSource;
uint32_t targetIfd = TIFF_IFD_0;
-
bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
-
if (hasThumbnail) {
ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
uint32_t thumbWidth = context->getThumbnailWidth();
- thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
+ thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_SUB1,
thumbWidth, context->getThumbnailHeight(), bytesPerPixel,
bytesPerPixel * thumbWidth, /*offset*/0, BYTES_PER_RGB_SAMPLE,
SAMPLES_PER_RGB_PIXEL);
- sources.add(thumbnailSource.get());
- targetIfd = TIFF_IFD_SUB1;
}
if (isDirect) {
@@ -2584,6 +2521,9 @@
DirectStripSource stripSource(env, pixelBytes, targetIfd, uWidth, uHeight, pStride,
rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
sources.add(&stripSource);
+ if (thumbnailSource.get() != nullptr) {
+ sources.add(thumbnailSource.get());
+ }
status_t ret = OK;
if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
@@ -2601,6 +2541,9 @@
InputStripSource stripSource(env, *inBuf, targetIfd, uWidth, uHeight, pStride,
rStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
sources.add(&stripSource);
+ if (thumbnailSource.get() != nullptr) {
+ sources.add(thumbnailSource.get());
+ }
status_t ret = OK;
if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
@@ -2612,6 +2555,7 @@
return;
}
}
+
}
static void DngCreator_nativeWriteInputStream(JNIEnv* env, jobject thiz, jobject outStream,
@@ -2654,20 +2598,8 @@
sp<DirectStripSource> thumbnailSource;
uint32_t targetIfd = TIFF_IFD_0;
- bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
Vector<StripSource*> sources;
- if (hasThumbnail) {
- ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
- uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
- uint32_t width = context->getThumbnailWidth();
- thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_0,
- width, context->getThumbnailHeight(), bytesPerPixel,
- bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
- SAMPLES_PER_RGB_PIXEL);
- sources.add(thumbnailSource.get());
- targetIfd = TIFF_IFD_SUB1;
- }
sp<JniInputStream> in = new JniInputStream(env, inStream);
@@ -2676,6 +2608,18 @@
rowStride, uOffset, BYTES_PER_SAMPLE, SAMPLES_PER_RAW_PIXEL);
sources.add(&stripSource);
+ bool hasThumbnail = writer->hasIfd(TIFF_IFD_SUB1);
+ if (hasThumbnail) {
+ ALOGV("%s: Adding thumbnail strip sources.", __FUNCTION__);
+ uint32_t bytesPerPixel = SAMPLES_PER_RGB_PIXEL * BYTES_PER_RGB_SAMPLE;
+ uint32_t width = context->getThumbnailWidth();
+ thumbnailSource = new DirectStripSource(env, context->getThumbnail(), TIFF_IFD_SUB1,
+ width, context->getThumbnailHeight(), bytesPerPixel,
+ bytesPerPixel * width, /*offset*/0, BYTES_PER_RGB_SAMPLE,
+ SAMPLES_PER_RGB_PIXEL);
+ sources.add(thumbnailSource.get());
+ }
+
status_t ret = OK;
if ((ret = writer->write(out.get(), sources.editArray(), sources.size())) != OK) {
ALOGE("%s: write failed with error %d.", __FUNCTION__, ret);
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index ab88b53..b20addb4 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -27,6 +27,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::WindowInfo;
namespace {
@@ -42,7 +43,8 @@
WindowInfosListener(JNIEnv* env, jobject listener)
: mListener(env->NewWeakGlobalRef(listener)) {}
- void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+ void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& /*displayInfos*/) override {
JNIEnv* env = AndroidRuntime::getJNIEnv();
LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onWindowInfoChanged.");
diff --git a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
index c0ecf33..689b259 100644
--- a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-#include "core_jni_helpers.h"
-
#include <cputimeinstate.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "LongArrayMultiStateCounter.h"
+#include "core_jni_helpers.h"
namespace android {
@@ -42,8 +44,70 @@
return copyVecsToArray(env, out.value());
}
+/**
+ * Computes delta of CPU time-in-freq from the previously supplied counts and adds the delta
+ * to the supplied multi-state counter in accordance with the counter's state.
+ */
+static jboolean addCpuTimeInFreqDelta(
+ jint uid, jlong counterNativePtr, jlong timestampMs,
+ std::optional<std::vector<std::vector<uint64_t>>> timeInFreqDataNanos) {
+ if (!timeInFreqDataNanos) {
+ return false;
+ }
+
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(counterNativePtr);
+ size_t s = 0;
+ for (const auto &cluster : *timeInFreqDataNanos) s += cluster.size();
+
+ std::vector<uint64_t> flattened;
+ flattened.reserve(s);
+ auto offset = flattened.begin();
+ for (const auto &cluster : *timeInFreqDataNanos) {
+ flattened.insert(offset, cluster.begin(), cluster.end());
+ offset += cluster.size();
+ }
+ for (size_t i = 0; i < s; ++i) {
+ flattened[i] /= NSEC_PER_MSEC;
+ }
+ counter->updateValue(flattened, timestampMs);
+ return true;
+}
+
+static jboolean addDeltaFromBpf(jint uid, jlong counterNativePtr, jlong timestampMs) {
+ return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
+ android::bpf::getUidCpuFreqTimes(uid));
+}
+
+static jboolean addDeltaForTest(JNIEnv *env, jclass, jint uid, jlong counterNativePtr,
+ jlong timestampMs, jobjectArray timeInFreqDataNanos) {
+ if (!timeInFreqDataNanos) {
+ return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs,
+ std::optional<std::vector<std::vector<uint64_t>>>());
+ }
+
+ std::vector<std::vector<uint64_t>> timeInFreqData;
+ jsize len = env->GetArrayLength(timeInFreqDataNanos);
+ for (jsize i = 0; i < len; i++) {
+ std::vector<uint64_t> cluster;
+ ScopedLongArrayRO row(env, (jlongArray)env->GetObjectArrayElement(timeInFreqDataNanos, i));
+ cluster.reserve(row.size());
+ for (size_t j = 0; j < row.size(); j++) {
+ cluster.push_back(row[j]);
+ }
+ timeInFreqData.push_back(cluster);
+ }
+ return addCpuTimeInFreqDelta(uid, counterNativePtr, timestampMs, std::optional(timeInFreqData));
+}
+
static const JNINativeMethod g_single_methods[] = {
- {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
+ {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
+
+ // @CriticalNative
+ {"addDeltaFromBpf", "(IJJ)Z", (void *)addDeltaFromBpf},
+
+ // Used for testing
+ {"addDeltaForTest", "(IJJ[[J)Z", (void *)addDeltaForTest},
};
int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 32c57d1..8326de2 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -14,17 +14,22 @@
* limitations under the License.
*/
+#include <android/binder_parcel.h>
+#include <android/binder_parcel_jni.h>
+#include <android/binder_parcel_utils.h>
+#include <android_runtime/Log.h>
#include <nativehelper/ScopedPrimitiveArray.h>
+
#include <cstring>
+
#include "LongArrayMultiStateCounter.h"
#include "core_jni_helpers.h"
namespace android {
-static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) {
+static jlong native_init(jint stateCount, jint arrayLength) {
battery::LongArrayMultiStateCounter *counter =
- new battery::LongArrayMultiStateCounter(stateCount, initialState,
- std::vector<uint64_t>(arrayLength), timestamp);
+ new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
return reinterpret_cast<jlong>(counter);
}
@@ -38,6 +43,12 @@
return reinterpret_cast<jlong>(native_dispose);
}
+static void native_setEnabled(jlong nativePtr, jboolean enabled, jlong timestamp) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ counter->setEnabled(enabled, timestamp);
+}
+
static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
@@ -54,6 +65,12 @@
counter->updateValue(*vector, timestamp);
}
+static void native_reset(jlong nativePtr) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ counter->reset();
+}
+
static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
@@ -69,23 +86,117 @@
return env->NewStringUTF(counter->toString().c_str());
}
+static void throwWriteRE(JNIEnv *env, binder_status_t status) {
+ ALOGE("Could not write LongArrayMultiStateCounter to Parcel, status = %d", status);
+ jniThrowRuntimeException(env, "Could not write LongArrayMultiStateCounter to Parcel");
+}
+
+#define THROW_ON_WRITE_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwWriteRE(env, status); \
+ } \
+ }
+
+static void native_writeToParcel(JNIEnv *env, jobject self, jlong nativePtr, jobject jParcel,
+ jint flags) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);
+
+ uint16_t stateCount = counter->getStateCount();
+ THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, stateCount));
+
+ // LongArrayMultiStateCounter has at least state 0
+ const std::vector<uint64_t> &anyState = counter->getCount(0);
+ THROW_ON_WRITE_ERROR(AParcel_writeInt32(parcel, anyState.size()));
+
+ for (battery::state_t state = 0; state < stateCount; state++) {
+ THROW_ON_WRITE_ERROR(ndk::AParcel_writeVector(parcel, counter->getCount(state)));
+ }
+}
+
+static void throwReadRE(JNIEnv *env, binder_status_t status) {
+ ALOGE("Could not read LongArrayMultiStateCounter from Parcel, status = %d", status);
+ jniThrowRuntimeException(env, "Could not read LongArrayMultiStateCounter from Parcel");
+}
+
+#define THROW_ON_READ_ERROR(expr) \
+ { \
+ binder_status_t status = expr; \
+ if (status != STATUS_OK) { \
+ throwReadRE(env, status); \
+ } \
+ }
+
+static jlong native_initFromParcel(JNIEnv *env, jclass theClass, jobject jParcel) {
+ AParcel *parcel = AParcel_fromJavaParcel(env, jParcel);
+
+ int32_t stateCount;
+ THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &stateCount));
+
+ int32_t arrayLength;
+ THROW_ON_READ_ERROR(AParcel_readInt32(parcel, &arrayLength));
+
+ battery::LongArrayMultiStateCounter *counter =
+ new battery::LongArrayMultiStateCounter(stateCount, std::vector<uint64_t>(arrayLength));
+
+ std::vector<uint64_t> value;
+ value.reserve(arrayLength);
+
+ for (battery::state_t state = 0; state < stateCount; state++) {
+ THROW_ON_READ_ERROR(ndk::AParcel_readVector(parcel, &value));
+ counter->setValue(state, value);
+ }
+
+ return reinterpret_cast<jlong>(counter);
+}
+
+static jint native_getStateCount(jlong nativePtr) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+ return counter->getStateCount();
+}
+
+static jint native_getArrayLength(jlong nativePtr) {
+ battery::LongArrayMultiStateCounter *counter =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
+
+ // LongArrayMultiStateCounter has at least state 0
+ const std::vector<uint64_t> &anyState = counter->getCount(0);
+ return anyState.size();
+}
+
static jlong native_init_LongArrayContainer(jint length) {
return reinterpret_cast<jlong>(new std::vector<uint64_t>(length));
}
static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
// @CriticalNative
- {"native_init", "(IIIJ)J", (void *)native_init},
+ {"native_init", "(II)J", (void *)native_init},
// @CriticalNative
{"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc},
// @CriticalNative
+ {"native_setEnabled", "(JZJ)V", (void *)native_setEnabled},
+ // @CriticalNative
{"native_setState", "(JIJ)V", (void *)native_setState},
// @CriticalNative
{"native_updateValues", "(JJJ)V", (void *)native_updateValues},
// @CriticalNative
+ {"native_reset", "(J)V", (void *)native_reset},
+ // @CriticalNative
{"native_getCounts", "(JJI)V", (void *)native_getCounts},
// @FastNative
{"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
+ // @FastNative
+ {"native_writeToParcel", "(JLandroid/os/Parcel;I)V", (void *)native_writeToParcel},
+ // @FastNative
+ {"native_initFromParcel", "(Landroid/os/Parcel;)J", (void *)native_initFromParcel},
+ // @CriticalNative
+ {"native_getStateCount", "(J)I", (void *)native_getStateCount},
+ // @CriticalNative
+ {"native_getArrayLength", "(J)I", (void *)native_getArrayLength},
};
/////////////////////// LongArrayMultiStateCounter.LongArrayContainer ////////////////////////
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 4af9d75..8e33561 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -211,6 +211,8 @@
optional DisplayRotationProto display_rotation = 33;
optional int32 ime_policy = 34;
+
+ repeated InsetsSourceProviderProto insets_source_providers = 35;
}
/* represents DisplayArea object */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8c28aec..e655fab 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -201,6 +201,9 @@
android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_DEVICE_AVAILABLE" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE" />
<protected-broadcast
android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
@@ -5884,7 +5887,6 @@
android:excludeFromRecents="true"
android:documentLaunchMode="never"
android:relinquishTaskIdentity="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:process=":ui"
android:visibleToInstantApps="true">
<intent-filter>
diff --git a/core/res/res/drawable-nodpi/default_wallpaper.png b/core/res/res/drawable-nodpi/default_wallpaper.png
index ce546f0..490ebee 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
deleted file mode 100644
index af8e251..0000000
--- a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
deleted file mode 100644
index cb00d82..0000000
--- a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout-watch/global_actions.xml b/core/res/res/layout-watch/global_actions.xml
index b7479d8..d8e569b 100644
--- a/core/res/res/layout-watch/global_actions.xml
+++ b/core/res/res/layout-watch/global_actions.xml
@@ -13,9 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:fillViewport="true">
+ <LinearLayout
+ android:id="@+id/actions_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:showDividers="middle"
android:divider="@drawable/global_action_item_divider"
android:orientation="vertical"/>
+</ScrollView>
diff --git a/core/res/res/layout/accessibility_shortcut_chooser_item.xml b/core/res/res/layout/accessibility_shortcut_chooser_item.xml
index 7cca129..4d7946b 100644
--- a/core/res/res/layout/accessibility_shortcut_chooser_item.xml
+++ b/core/res/res/layout/accessibility_shortcut_chooser_item.xml
@@ -39,15 +39,20 @@
android:layout_height="48dp"
android:scaleType="fitCenter"/>
- <TextView
- android:id="@+id/accessibility_shortcut_target_label"
+ <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_weight="1"
- android:textSize="20sp"
- android:textColor="?attr/textColorPrimary"
- android:fontFamily="sans-serif-medium"/>
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/accessibility_shortcut_target_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textColor="?attr/textColorPrimary"
+ android:fontFamily="sans-serif-medium"/>
+ </LinearLayout>
<TextView
android:id="@+id/accessibility_shortcut_target_status"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d11c3f7..790a2a3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4570,6 +4570,20 @@
-->
</integer-array>
+ <!-- An array of arrays of side fingerprint sensor properties relative to each display.
+ Note: this value is temporary and is expected to be queried directly
+ from the HAL in the future. -->
+ <array name="config_sfps_sensor_props" translatable="false">
+ <!--
+ <array>
+ <item>displayId</item>
+ <item>sensorLocationX</item>
+ <item>sensorLocationY</item>
+ <item>sensorRadius</item>
+ <array>
+ -->
+ </array>
+
<!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
<integer name="config_udfps_illumination_transition_ms">50</integer>
@@ -4883,6 +4897,21 @@
or > 1, it is ignored and central positionis used (0.5). -->
<item name="config_letterboxHorizontalPositionMultiplier" format="float" type="dimen">0.5</item>
+ <!-- Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
+ device orientation. -->
+ <bool name="config_letterboxIsReachabilityEnabled">false</bool>
+
+ <!-- Default horizonal position of a center of the letterboxed app window when reachability is
+ enabled and an app is fullscreen in landscape device orientation.
+ 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0.0
+ or > 1, it is ignored and right positionis used (1.0). The position multiplier is changed
+ to a symmetrical value computed as (1 - current multiplier) after each double tap in the
+ letterbox area. -->
+ <item name="config_letterboxDefaultPositionMultiplierForReachability"
+ format="float" type="dimen">
+ 0.9
+ </item>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -5186,6 +5215,12 @@
<item>@array/config_secondaryBuiltInDisplayWaterfallCutout</item>
</array>
+ <!-- The component name of the activity for the companion-device-manager notification access
+ confirmation. -->
+ <string name="config_notificationAccessConfirmationActivity" translatable="false">
+ com.android.settings/com.android.settings.notification.NotificationAccessConfirmationActivity
+ </string>
+
<!-- Whether the airplane mode should be reset when device boots in non-safemode after exiting
from safemode.
This flag should be enabled only when the product does not have any UI to toggle airplane
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5df7a97..b0c0a15 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2224,6 +2224,7 @@
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
+ <java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -2610,6 +2611,7 @@
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" />
+ <java-symbol type="array" name="config_sfps_sensor_props" />
<java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" />
@@ -4255,6 +4257,8 @@
<java-symbol type="integer" name="config_letterboxBackgroundType" />
<java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
+ <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
+ <java-symbol type="dimen" name="config_letterboxDefaultPositionMultiplierForReachability" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 6f17ea9..1f27063 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -108,6 +108,11 @@
}
@Test
+ public void testTemporaryDirectory() throws Exception {
+ assertEquals(System.getProperty("java.io.tmpdir"), System.getenv("TMPDIR"));
+ }
+
+ @Test
public void testDoubleRelaunch() throws Exception {
final Activity activity = mActivityTestRule.launchActivity(new Intent());
final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index a5a98a9..109b7ab 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -17,6 +17,8 @@
package android.service.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
@@ -51,6 +53,7 @@
private final Context mMockContext = mock(Context.class);
@Mock
+ private Context mRealContext;
private PackageManager mPm;
private static final String PKG = "com.example.o";
@@ -75,6 +78,8 @@
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+
+ mRealContext = InstrumentationRegistry.getContext();
}
@Test
@@ -199,6 +204,19 @@
}
+ @Test
+ public void testGetPackageContext_worksWithUserAll() {
+ String pkg = "com.android.systemui";
+ int uid = 1000;
+ Notification notification = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID).build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ pkg, pkg, ID, TAG, uid, uid, notification, UserHandle.ALL, null, UID);
+ Context resultContext = sbn.getPackageContext(mRealContext);
+ assertNotNull(resultContext);
+ assertNotSame(mRealContext, resultContext);
+ assertEquals(pkg, resultContext.getPackageName());
+ }
+
private StatusBarNotification getNotification(String pkg, String group, String channelId) {
return getNotification(pkg, getNotificationBuilder(group, channelId));
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index f833981..0e394c1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -403,7 +403,7 @@
assertNotNull(sensor.getSensorBackgroundTime());
// Reset the stats. Since the sensor is still running, we should still see the timer
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
sensor = uid.getSensorStats().get(SENSOR_ID);
assertNotNull(sensor);
@@ -413,7 +413,7 @@
bi.noteStopSensorLocked(UID, SENSOR_ID);
// Now the sensor timer has stopped so this reset should also take out the sensor.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
sensor = uid.getSensorStats().get(SENSOR_ID);
assertNull(sensor);
@@ -465,7 +465,7 @@
// Reset the stats. Since the sensor is still running, we should still see the timer
// but still with 0 times.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
@@ -504,7 +504,7 @@
// Reset the stats. Since the sensor is still running, we should still see the timer
// but with 0 times.
- bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000);
+ bi.getUidStatsLocked(UID).reset(clocks.uptime * 1000, clocks.realtime * 1000, 0);
assertEquals(0, timer.getTotalTimeLocked(1000*clocks.realtime, which));
assertEquals(0, timer.getTotalDurationMsLocked(clocks.realtime));
assertEquals(0, bgTimer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index dac35e5..4182574 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -24,7 +26,6 @@
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelSingleUidTimeReader.Injector;
@@ -279,6 +280,50 @@
0, lastUidCpuTimes.size());
}
+ @Test
+ public void testAddDeltaFromBpf() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5);
+ counter.setState(0, 0);
+ mInjector.setCpuTimeInStatePerClusterNs(new long[][]{{0, 0, 0}, {0, 0}});
+ boolean success = mInjector.addDelta(TEST_UID, counter, 0);
+ assertThat(success).isTrue();
+
+ // Nanoseconds
+ mInjector.setCpuTimeInStatePerClusterNs(
+ new long[][]{
+ {1_000_000, 2_000_000, 3_000_000},
+ {4_000_000, 5_000_000}});
+
+ success = mInjector.addDelta(TEST_UID, counter, 2000);
+ assertThat(success).isTrue();
+
+ LongArrayMultiStateCounter.LongArrayContainer array =
+ new LongArrayMultiStateCounter.LongArrayContainer(5);
+ long[] out = new long[5];
+
+ counter.getCounts(array, 0);
+ array.getValues(out);
+ assertThat(out).isEqualTo(new long[]{1, 2, 3, 4, 5});
+
+ counter.setState(1, 3000);
+
+ mInjector.setCpuTimeInStatePerClusterNs(
+ new long[][]{
+ {11_000_000, 22_000_000, 33_000_000},
+ {44_000_000, 55_000_000}});
+
+ success = mInjector.addDelta(TEST_UID, counter, 4000);
+ assertThat(success).isTrue();
+
+ counter.getCounts(array, 0);
+ array.getValues(out);
+ assertThat(out).isEqualTo(new long[]{1 + 5, 2 + 10, 3 + 15, 4 + 20, 5 + 25});
+
+ counter.getCounts(array, 1);
+ array.getValues(out);
+ assertThat(out).isEqualTo(new long[]{5, 10, 15, 20, 25});
+ }
+
private void assertCpuTimesEqual(long[] expected, long[] actual) {
assertArrayEquals("Expected=" + Arrays.toString(expected)
+ ", Actual=" + Arrays.toString(actual), expected, actual);
@@ -288,6 +333,7 @@
private byte[] mData;
private long[] mBpfData;
private boolean mThrowExcpetion;
+ private long[][] mCpuTimeInStatePerClusterNs;
@Override
public byte[] readData(String procFile) throws IOException {
@@ -316,8 +362,17 @@
mBpfData = cpuTimes.clone();
}
+ public void setCpuTimeInStatePerClusterNs(long[][] cpuTimeInStatePerClusterNs) {
+ mCpuTimeInStatePerClusterNs = cpuTimeInStatePerClusterNs;
+ }
+
public void letReadDataThrowException(boolean throwException) {
mThrowExcpetion = throwException;
}
+
+ @Override
+ public boolean addDelta(int uid, LongArrayMultiStateCounter counter, long timestampMs) {
+ return addDeltaForTest(uid, counter, timestampMs, mCpuTimeInStatePerClusterNs);
+ }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 2da9f57..9eb4ccb 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import android.os.Parcel;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -30,26 +32,121 @@
@Test
public void setStateAndUpdateValue() {
- LongArrayMultiStateCounter.LongArrayContainer longArrayContainer =
- new LongArrayMultiStateCounter.LongArrayContainer(4);
- LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+
+ updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ counter.setState(0, 1000);
counter.setState(1, 2000);
counter.setState(0, 4000);
- longArrayContainer.setValues(new long[]{100, 200, 300, 400});
- counter.updateValues(longArrayContainer, 9000);
- counter.getCounts(longArrayContainer, 0);
+ updateValue(counter, new long[]{100, 200, 300, 400}, 9000);
- long[] result = new long[4];
- longArrayContainer.getValues(result);
- assertThat(result).isEqualTo(new long[]{75, 150, 225, 300});
-
- counter.getCounts(longArrayContainer, 1);
- longArrayContainer.getValues(result);
- assertThat(result).isEqualTo(new long[]{25, 50, 75, 100});
+ assertCounts(counter, 0, new long[]{75, 150, 225, 300});
+ assertCounts(counter, 1, new long[]{25, 50, 75, 100});
assertThat(counter.toString()).isEqualTo(
- "currentState: 0 lastStateChangeTimestamp: 9000 lastUpdateTimestamp: 9000 states:"
- + " [0: time: 0 counter: { 75, 150, 225, 300}"
- + ", 1: time: 0 counter: { 25, 50, 75, 100}]");
+ "[0: {75, 150, 225, 300}, 1: {25, 50, 75, 100}] updated: 9000 currentState: 0");
+ }
+
+ @Test
+ public void setEnabled() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+ counter.setState(0, 1000);
+ updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+
+ assertCounts(counter, 0, new long[]{100, 200, 300, 400});
+
+ counter.setEnabled(false, 3000);
+
+ // Partially included, because the counter is disabled after the previous update
+ updateValue(counter, new long[]{200, 300, 400, 500}, 4000);
+
+ // Count only 50%, because the counter was disabled for 50% of the time
+ assertCounts(counter, 0, new long[]{150, 250, 350, 450});
+
+ // Not counted because the counter is disabled
+ updateValue(counter, new long[]{250, 350, 450, 550}, 5000);
+
+ counter.setEnabled(true, 6000);
+
+ updateValue(counter, new long[]{300, 400, 500, 600}, 7000);
+
+ // Again, take 50% of the delta
+ assertCounts(counter, 0, new long[]{175, 275, 375, 475});
+ }
+
+ @Test
+ public void reset() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+ counter.setState(0, 1000);
+ updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+
+ assertCounts(counter, 0, new long[]{100, 200, 300, 400});
+
+ counter.reset();
+
+ assertCounts(counter, 0, new long[]{0, 0, 0, 0});
+
+ updateValue(counter, new long[]{200, 300, 400, 500}, 3000);
+ updateValue(counter, new long[]{300, 400, 500, 600}, 4000);
+
+ assertCounts(counter, 0, new long[]{100, 100, 100, 100});
+ }
+
+ @Test
+ public void parceling() {
+ LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
+ updateValue(counter, new long[]{0, 0, 0, 0}, 1000);
+ counter.setState(0, 1000);
+ updateValue(counter, new long[]{100, 200, 300, 400}, 2000);
+ counter.setState(1, 2000);
+ updateValue(counter, new long[]{101, 202, 304, 408}, 3000);
+
+ assertCounts(counter, 0, new long[]{100, 200, 300, 400});
+ assertCounts(counter, 1, new long[]{1, 2, 4, 8});
+
+ Parcel parcel = Parcel.obtain();
+ counter.writeToParcel(parcel, 0);
+ byte[] bytes = parcel.marshall();
+ parcel.recycle();
+
+ parcel = Parcel.obtain();
+ parcel.unmarshall(bytes, 0, bytes.length);
+ parcel.setDataPosition(0);
+
+ LongArrayMultiStateCounter newCounter =
+ LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ assertCounts(newCounter, 0, new long[]{100, 200, 300, 400});
+ assertCounts(newCounter, 1, new long[]{1, 2, 4, 8});
+
+ // ==== Verify that the counter keeps accumulating after unparceling.
+
+ // State, last update timestamp and current counts are undefined at this point.
+ newCounter.setState(0, 100);
+ updateValue(newCounter, new long[]{300, 400, 500, 600}, 100);
+
+ // A new base state and counters are established; we can continue accumulating deltas
+ updateValue(newCounter, new long[]{316, 432, 564, 728}, 200);
+
+ assertCounts(newCounter, 0, new long[]{116, 232, 364, 528});
+ }
+
+ private void updateValue(LongArrayMultiStateCounter counter, long[] values, int timestamp) {
+ LongArrayMultiStateCounter.LongArrayContainer container =
+ new LongArrayMultiStateCounter.LongArrayContainer(values.length);
+ container.setValues(values);
+ counter.updateValues(container, timestamp);
+ }
+
+ private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) {
+ LongArrayMultiStateCounter.LongArrayContainer container =
+ new LongArrayMultiStateCounter.LongArrayContainer(expected.length);
+ long[] counts = new long[expected.length];
+ counter.getCounts(container, state);
+ container.getValues(counts);
+ assertThat(counts).isEqualTo(expected);
}
}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 42d80e5..8705067 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -48,6 +48,7 @@
<permission name="android.permission.SET_ACTIVITY_WATCHER"/>
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.car.permission.CONTROL_APP_BLOCKING"/>
<!-- use for rotary fragment to enable/disable packages related to rotary -->
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0d7225b..119c9391 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1207,6 +1207,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-779535710": {
+ "message": "Transition %d: Set %s as transient-launch",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-775004869": {
"message": "Not a match: %s",
"level": "DEBUG",
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 8f175bb..1e68585 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -160,7 +160,7 @@
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
*
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
- * from the verifier. The content can be defined in the way appropriate for the credential, byt
+ * from the verifier. The content can be defined in the way appropriate for the credential, but
* there are three requirements that must be met to work with this API:
* <ul>
* <li>The content must be a CBOR-encoded structure.</li>
@@ -205,9 +205,9 @@
* must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
* coordinate.
*
- * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
- * structure as defined in RFC 8152. For the payload nil shall be used and the
- * detached payload is the ReaderAuthenticationBytes CBOR described below.
+ * <p>If {@code readerSignature} is not {@code null} it must be the bytes of a
+ * {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and
+ * the detached payload is the ReaderAuthenticationBytes CBOR described below.
* <pre>
* ReaderAuthentication = [
* "ReaderAuthentication",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
index 9212a0f..46c8ffe 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -210,17 +210,17 @@
void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) {
- WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null;
+ WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null;
final boolean finishSecondaryWithPrimary =
splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule);
final boolean finishPrimaryWithSecondary =
splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule);
if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) {
- adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions();
- adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary);
- adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary);
+ adjacentParams = new WindowContainerTransaction.TaskFragmentAdjacentParams();
+ adjacentParams.setShouldDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary);
+ adjacentParams.setShouldDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary);
}
- wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions);
+ wct.setAdjacentTaskFragments(primary, secondary, adjacentParams);
}
TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index e0654bd..ad5a68e40 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -61,7 +61,7 @@
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
- <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચેટ કરો"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચૅટ કરો"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"બબલને કોઈપણ સમયે નિયંત્રિત કરો"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"આ ઍપમાંથી બબલને બંધ કરવા માટે મેનેજ કરો પર ટૅપ કરો"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 9c97ffe..3c8aaa4 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 9c97ffe..3c8aaa4 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b5dffba..d326534 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -340,6 +340,13 @@
}
@Override
+ public void onImeDrawnOnTask(int taskId) {
+ if (mStartingWindow != null) {
+ mStartingWindow.onImeDrawnOnTask(taskId);
+ }
+ }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 3800b8d..c2cb72a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitWindowManager;
import java.io.PrintWriter;
@@ -70,6 +71,19 @@
private final DisplayImeController mDisplayImeController;
private SplitLayout mSplitLayout;
+ private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+ new SplitWindowManager.ParentContainerCallbacks() {
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ b.setParent(mRootTaskLeash);
+ }
+
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> t.show(leash));
+ }
+ };
+
AppPair(AppPairsController controller) {
mController = controller;
mSyncQueue = controller.getSyncTransactionQueue();
@@ -110,8 +124,7 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
- b -> b.setParent(mRootTaskLeash), mDisplayImeController,
- mController.getTaskOrganizer());
+ mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
@@ -218,8 +231,6 @@
if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
onLayoutChanged(mSplitLayout);
}
- // updateConfiguration re-inits the dividerbar, so show it now
- mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 9d65d28..05ebbba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -422,14 +422,6 @@
}
}
- @Override
- public void setExpandedContentAlpha(float alpha) {
- if (mExpandedView != null) {
- mExpandedView.setAlpha(alpha);
- mExpandedView.setTaskViewAlpha(alpha);
- }
- }
-
/**
* Set visibility of bubble in the expanded state.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 9dafefe..c126f32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -69,6 +69,7 @@
import android.util.SparseSetArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.WindowContainerTransaction;
@@ -144,7 +145,6 @@
private BubbleLogger mLogger;
private BubbleData mBubbleData;
- private View mBubbleScrim;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
private BubblePositioner mBubblePositioner;
@@ -189,6 +189,9 @@
/** Saved direction, used to detect layout direction changes @link #onConfigChanged}. */
private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+ /** Saved insets, used to detect WindowInset changes. */
+ private WindowInsets mWindowInsets;
+
private boolean mInflateSynchronously;
/** True when user is in status bar unlock shade. */
@@ -629,8 +632,11 @@
mBubbleData.getOverflow().initialize(this);
mWindowManager.addView(mStackView, mWmLayoutParams);
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- mBubblePositioner.update();
- mStackView.onDisplaySizeChanged();
+ if (!windowInsets.equals(mWindowInsets)) {
+ mWindowInsets = windowInsets;
+ mBubblePositioner.update();
+ mStackView.onDisplaySizeChanged();
+ }
return windowInsets;
});
} catch (IllegalStateException e) {
@@ -1372,8 +1378,9 @@
private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ mBubblePositioner.setImeVisible(imeVisible, imeHeight);
if (mStackView != null) {
- mStackView.onImeVisibilityChanged(imeVisible, imeHeight);
+ mStackView.animateForIme(imeVisible);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 7d7bfb2..a87aad4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -398,6 +398,7 @@
updatePointerView();
}
+ /** Updates the size and visuals of the pointer. **/
private void updatePointerView() {
LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams();
if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) {
@@ -524,9 +525,8 @@
if (mTaskView != null) {
mTaskView.setAlpha(alpha);
}
- if (mManageButton != null && mManageButton.getVisibility() == View.VISIBLE) {
- mManageButton.setAlpha(alpha);
- }
+ mPointerView.setAlpha(alpha);
+ setAlpha(alpha);
}
/**
@@ -545,6 +545,7 @@
mIsContentVisible = visibility;
if (mTaskView != null && !mIsAlphaAnimating) {
mTaskView.setAlpha(visibility ? 1f : 0f);
+ mPointerView.setAlpha(visibility ? 1f : 0f);
}
}
@@ -689,7 +690,7 @@
* the bubble if showing vertically.
* @param onLeft whether the stack was on the left side of the screen when expanded.
*/
- public void setPointerPosition(float bubblePosition, boolean onLeft) {
+ public void setPointerPosition(float bubblePosition, boolean onLeft, boolean animate) {
// Pointer gets drawn in the padding
final boolean showVertically = mPositioner.showBubblesVertically();
final float paddingLeft = (showVertically && onLeft)
@@ -710,6 +711,8 @@
: pointerPosition;
// Post because we need the width of the view
post(() -> {
+ mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
+ updatePointerView();
float pointerY;
float pointerX;
if (showVertically) {
@@ -721,11 +724,13 @@
pointerY = mPointerOverlap;
pointerX = bubbleCenter - (mPointerWidth / 2f);
}
- mPointerView.setTranslationY(pointerY);
- mPointerView.setTranslationX(pointerX);
- mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
- updatePointerView();
- mPointerView.setVisibility(VISIBLE);
+ if (animate) {
+ mPointerView.animate().translationX(pointerX).translationY(pointerY).start();
+ } else {
+ mPointerView.setTranslationY(pointerY);
+ mPointerView.setTranslationX(pointerX);
+ mPointerView.setVisibility(VISIBLE);
+ }
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 705a12a..0c3a6b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -154,10 +154,6 @@
return dotPath
}
- override fun setExpandedContentAlpha(alpha: Float) {
- expandedView?.alpha = alpha
- }
-
override fun setTaskViewVisibility(visible: Boolean) {
// Overflow does not have a TaskView.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 306224b..127d5a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -70,13 +70,16 @@
private Context mContext;
private WindowManager mWindowManager;
- private Rect mPositionRect;
private Rect mScreenRect;
private @Surface.Rotation int mRotation = Surface.ROTATION_0;
private Insets mInsets;
+ private boolean mImeVisible;
+ private int mImeHeight;
+ private boolean mIsLargeScreen;
+
+ private Rect mPositionRect;
private int mDefaultMaxBubbles;
private int mMaxBubbles;
-
private int mBubbleSize;
private int mSpacingBetweenBubbles;
@@ -98,7 +101,6 @@
private PointF mRestingStackPosition;
private int[] mPaddings = new int[4];
- private boolean mIsLargeScreen;
private boolean mShowingInTaskbar;
private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE;
private int mTaskbarIconSize;
@@ -302,6 +304,17 @@
return mMaxBubbles;
}
+ /** The height for the IME if it's visible. **/
+ public int getImeHeight() {
+ return mImeVisible ? mImeHeight : 0;
+ }
+
+ /** Sets whether the IME is visible. **/
+ public void setImeVisible(boolean visible, int height) {
+ mImeVisible = visible;
+ mImeHeight = height;
+ }
+
/**
* Calculates the padding for the bubble expanded view.
*
@@ -357,7 +370,7 @@
}
/** Gets the y position of the expanded view if it was top-aligned. */
- private float getExpandedViewYTopAligned() {
+ public float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
return top - mPointerWidth + mExpandedViewPadding;
@@ -366,10 +379,6 @@
}
}
- public float getExpandedBubblesY() {
- return getAvailableRect().top + mExpandedViewPadding;
- }
-
/**
* Calculate the maximum height the expanded view can be depending on where it's placed on
* the screen and the size of the elements around it (e.g. padding, pointer, manage button).
@@ -464,18 +473,21 @@
: bubblePosition + (normalizedSize / 2f) - mPointerWidth;
}
+ private int getExpandedStackSize(int numberOfBubbles) {
+ return (numberOfBubbles * mBubbleSize)
+ + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ }
+
/**
* Returns the position of the bubble on-screen when the stack is expanded.
*
* @param index the index of the bubble in the stack.
- * @param numberOfBubbles the total number of bubbles in the stack.
- * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed.
- * @return the x, y position of the bubble on-screen when the stack is expanded.
+ * @param state state information about the stack to help with calculations.
+ * @return the position of the bubble on-screen when the stack is expanded.
*/
- public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) {
+ public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) {
final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
- final float expandedStackSize = (numberOfBubbles * mBubbleSize)
- + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ final float expandedStackSize = getExpandedStackSize(state.numberOfBubbles);
final float centerPosition = showBubblesVertically()
? mPositionRect.centerY()
: mPositionRect.centerX();
@@ -491,17 +503,66 @@
int right = mIsLargeScreen
? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
: mPositionRect.right - mBubbleSize;
- x = onLeftEdge
+ x = state.onLeft
? left
: right;
} else {
y = mPositionRect.top + mExpandedViewPadding;
x = rowStart + positionInRow;
}
+
+ if (showBubblesVertically() && mImeVisible) {
+ return new PointF(x, getExpandedBubbleYForIme(index, state.numberOfBubbles));
+ }
return new PointF(x, y);
}
/**
+ * Returns the position of the bubble on-screen when the stack is expanded and the IME
+ * is showing.
+ *
+ * @param index the index of the bubble in the stack.
+ * @param numberOfBubbles the total number of bubbles in the stack.
+ * @return y position of the bubble on-screen when the stack is expanded.
+ */
+ private float getExpandedBubbleYForIme(int index, int numberOfBubbles) {
+ final float top = getAvailableRect().top + mExpandedViewPadding;
+ if (!showBubblesVertically()) {
+ // Showing horizontally: align to top
+ return top;
+ }
+
+ // Showing vertically: might need to translate the bubbles above the IME.
+ // Subtract spacing here to provide a margin between top of IME and bottom of bubble row.
+ final float bottomInset = getImeHeight() + mInsets.bottom - (mSpacingBetweenBubbles * 2);
+ final float expandedStackSize = getExpandedStackSize(numberOfBubbles);
+ final float centerPosition = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ final float rowBottom = centerPosition + (expandedStackSize / 2f);
+ final float rowTop = centerPosition - (expandedStackSize / 2f);
+ float rowTopForIme = rowTop;
+ if (rowBottom > bottomInset) {
+ // We overlap with IME, must shift the bubbles
+ float translationY = rowBottom - bottomInset;
+ rowTopForIme = Math.max(rowTop - translationY, top);
+ if (rowTop - translationY < top) {
+ // Even if we shift the bubbles, they will still overlap with the IME.
+ // Hide the overflow for a lil more space:
+ final float expandedStackSizeNoO = getExpandedStackSize(numberOfBubbles - 1);
+ final float centerPositionNoO = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ final float rowBottomNoO = centerPositionNoO + (expandedStackSizeNoO / 2f);
+ final float rowTopNoO = centerPositionNoO - (expandedStackSizeNoO / 2f);
+ translationY = rowBottomNoO - bottomInset;
+ rowTopForIme = rowTopNoO - translationY;
+ }
+ }
+ return rowTopForIme + (index * (mBubbleSize + mSpacingBetweenBubbles));
+ }
+
+ /**
* @return the width of the bubble flyout (message originating from the bubble).
*/
public float getMaxFlyoutSize() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 5bc6128..9d0dd3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -28,6 +28,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
@@ -188,6 +190,7 @@
};
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
+ private StackViewState mStackViewState = new StackViewState();
private final ValueAnimator mDismissBubbleAnimator;
@@ -246,7 +249,6 @@
private int mBubbleTouchPadding;
private int mExpandedViewPadding;
private int mCornerRadius;
- private int mImeOffset;
@Nullable private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded;
@@ -757,7 +759,6 @@
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
- mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -779,7 +780,7 @@
this::animateShadows /* onStackAnimationFinished */, mPositioner);
mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
- onBubbleAnimatedOut);
+ onBubbleAnimatedOut, this);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -890,7 +891,7 @@
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
updateOverflowVisibility();
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
} /* after */);
@@ -968,7 +969,8 @@
});
mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
if (mExpandedBubble != null) {
- mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue());
+ mExpandedBubble.getExpandedView().setTaskViewAlpha(
+ (float) valueAnimator.getAnimatedValue());
}
});
@@ -1558,7 +1560,7 @@
} else {
bubble.cleanupViews();
}
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
@@ -1599,7 +1601,7 @@
.map(b -> b.getIconView()).collect(Collectors.toList());
mStackAnimationController.animateReorder(bubbleViews, reorder);
}
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
}
/**
@@ -1670,7 +1672,6 @@
private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) {
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
- updatePointerPosition();
if (mIsExpanded) {
hideCurrentInputMethod();
@@ -1762,6 +1763,7 @@
* not.
*/
void hideCurrentInputMethod() {
+ mPositioner.setImeVisible(false, 0);
mBubbleController.hideCurrentInputMethod();
}
@@ -1864,7 +1866,7 @@
updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
mExpandedAnimationController.expandFromStack(() -> {
if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
@@ -1876,8 +1878,7 @@
} else {
index = getBubbleIndex(mExpandedBubble);
}
- PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(),
- mStackOnLeftOrWillBe);
+ PointF p = mPositioner.getExpandedBubbleXY(index, getState());
final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x);
mExpandedViewContainer.setTranslationX(0f);
@@ -1928,7 +1929,7 @@
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
if (mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.setExpandedContentAlpha(0f);
+ mExpandedBubble.getExpandedView().setTaskViewAlpha(0f);
// We'll be starting the alpha animation after a slight delay, so set this flag early
// here.
@@ -2011,8 +2012,7 @@
index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
}
// Value the bubble is animating from (back into the stack).
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, getState());
if (mPositioner.showBubblesVertically()) {
float pivotX;
float pivotY = p.y + mBubbleSize / 2f;
@@ -2109,7 +2109,7 @@
PointF p = mPositioner.getExpandedBubbleXY(isOverflow
? mBubbleContainer.getChildCount() - 1
: mBubbleData.getBubbles().indexOf(mExpandedBubble),
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ getState());
mExpandedViewContainer.setAlpha(1f);
mExpandedViewContainer.setVisibility(View.VISIBLE);
@@ -2187,9 +2187,20 @@
}
}
- /** Moves the bubbles out of the way if they're going to be over the keyboard. */
- public void onImeVisibilityChanged(boolean visible, int height) {
- mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
+ /**
+ * Updates the stack based for IME changes. When collapsed it'll move the stack if it
+ * overlaps where they IME would be. When expanded it'll shift the expanded bubbles
+ * if they might overlap with the IME (this only happens for large screens).
+ */
+ public void animateForIme(boolean visible) {
+ if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
+ // This will update the animation so the bubbles move to position for the IME
+ mExpandedAnimationController.expandFromStack(() -> {
+ updatePointerPosition(false /* forIme */);
+ afterExpandedViewAnimation();
+ } /* after */);
+ return;
+ }
if (!mIsExpanded && getBubbleCount() > 0) {
final float stackDestinationY =
@@ -2208,9 +2219,20 @@
FLYOUT_IME_ANIMATION_SPRING_CONFIG)
.start();
}
- } else if (mIsExpanded && mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null) {
+ } else if (mPositioner.showBubblesVertically() && mIsExpanded
+ && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
mExpandedBubble.getExpandedView().setImeVisible(visible);
+ List<Animator> animList = new ArrayList();
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ View child = mBubbleContainer.getChildAt(i);
+ float transY = mPositioner.getExpandedBubbleXY(i, getState()).y;
+ ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY);
+ animList.add(anim);
+ }
+ updatePointerPosition(true /* forIme */);
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animList);
+ set.start();
}
}
@@ -2514,7 +2536,7 @@
// Account for the IME in the touchable region so that the touchable region of the
// Bubble window doesn't obscure the IME. The touchable region affects which areas
// of the screen can be excluded by lower windows (IME is just above the embedded task)
- outRect.bottom -= (int) mStackAnimationController.getImeHeight();
+ outRect.bottom -= mPositioner.getImeHeight();
}
if (mFlyout.getVisibility() == View.VISIBLE) {
@@ -2773,13 +2795,13 @@
}
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ getState());
mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x));
mExpandedViewContainer.setTranslationX(0f);
mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen());
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
@@ -2850,7 +2872,13 @@
}
}
- private void updatePointerPosition() {
+ /**
+ * Updates the position of the pointer based on the expanded bubble.
+ *
+ * @param forIme whether the position is being updated due to the ime appearing, in this case
+ * the pointer is animated to the location.
+ */
+ private void updatePointerPosition(boolean forIme) {
if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
return;
}
@@ -2858,13 +2886,12 @@
if (index == -1) {
return;
}
- PointF bubblePosition = mPositioner.getExpandedBubbleXY(index,
- mBubbleContainer.getChildCount(),
- mStackOnLeftOrWillBe);
- mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically()
- ? bubblePosition.y
- : bubblePosition.x,
- mStackOnLeftOrWillBe);
+ PointF position = mPositioner.getExpandedBubbleXY(index, getState());
+ float bubblePosition = mPositioner.showBubblesVertically()
+ ? position.y
+ : position.x;
+ mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition,
+ mStackOnLeftOrWillBe, forIme /* animate */);
}
/**
@@ -2947,6 +2974,26 @@
return bubbles;
}
+ /** @return the current stack state. */
+ public StackViewState getState() {
+ mStackViewState.numberOfBubbles = mBubbleContainer.getChildCount();
+ mStackViewState.selectedIndex = getBubbleIndex(mExpandedBubble);
+ mStackViewState.onLeft = mStackOnLeftOrWillBe;
+ return mStackViewState;
+ }
+
+ /**
+ * Holds some commonly queried information about the stack.
+ */
+ public static class StackViewState {
+ // Number of bubbles (including the overflow itself) in the stack.
+ public int numberOfBubbles;
+ // The selected index if the stack is expanded.
+ public int selectedIndex;
+ // Whether the stack is resting on the left or right side of the screen when collapsed.
+ public boolean onLeft;
+ }
+
/**
* Representation of stack position that uses relative properties rather than absolute
* coordinates. This is used to maintain similar stack positions across configuration changes.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 38b3ba9..7e55282 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -29,12 +29,6 @@
@Nullable BubbleExpandedView getExpandedView();
/**
- * Sets the alpha of the expanded view content. This will be applied to both the expanded view
- * container itself (the manage button, etc.) as well as the TaskView within it.
- */
- void setExpandedContentAlpha(float alpha);
-
- /**
* Sets whether the contents of the bubble's TaskView should be visible.
*/
void setTaskViewVisibility(boolean visible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index c32be98..f0f78748 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -21,7 +21,6 @@
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
@@ -33,6 +32,7 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.google.android.collect.Sets;
@@ -124,12 +124,15 @@
private BubblePositioner mPositioner;
+ private BubbleStackView mBubbleStackView;
+
public ExpandedAnimationController(BubblePositioner positioner,
- Runnable onBubbleAnimatedOutAction) {
+ Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) {
mPositioner = positioner;
updateResources();
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
mCollapsePoint = mPositioner.getDefaultStartPosition();
+ mBubbleStackView = stackView;
}
/**
@@ -239,10 +242,7 @@
final Path path = new Path();
path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
- boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mLayout.getChildCount(),
- onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
if (expanding) {
// If we're expanding, first draw a line from the bubble's current position to where
// it'll end up
@@ -364,7 +364,7 @@
bubbleView.setTranslationY(y);
}
- final float expandedY = mPositioner.getExpandedBubblesY();
+ final float expandedY = mPositioner.getExpandedViewYTopAligned();
final boolean draggedOutEnough =
y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) {
@@ -410,8 +410,7 @@
return;
}
final int index = mLayout.indexOfChild(bubbleView);
- final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(),
- mPositioner.isStackOnLeft(mCollapsePoint));
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
animationForChildAtIndex(index)
.position(p.x, p.y)
.withPositionStartVelocities(velX, velY)
@@ -429,16 +428,6 @@
updateBubblePositions();
}
- /**
- * Animates the bubbles to the y position. Used in response to IME showing.
- */
- public void updateYPosition(Runnable after) {
- if (mLayout == null) return;
- animationsForChildrenFromIndex(
- 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY()))
- .startAll(after);
- }
-
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("ExpandedAnimationController state:");
@@ -496,36 +485,33 @@
startOrUpdatePathAnimation(false /* expanding */);
} else {
boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mLayout.getChildCount(),
- onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
if (mPositioner.showBubblesVertically()) {
child.setTranslationY(p.y);
} else {
child.setTranslationX(p.x);
}
- if (!mPreparingToCollapse) {
- // Only animate if we're not collapsing as that animation will handle placing the
+
+ if (mPreparingToCollapse) {
+ // Don't animate if we're collapsing, as that animation will handle placing the
// new bubble in the stacked position.
- if (mPositioner.showBubblesVertically()) {
- Rect availableRect = mPositioner.getAvailableRect();
- float fromX = onLeft
- ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
- : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
- animationForChild(child)
- .translationX(fromX, p.y)
- .start();
- } else {
- // Only animate if we're not collapsing as that animation will handle placing
- // the new bubble in the stacked position.
- float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx
- * ANIMATE_TRANSLATION_FACTOR;
- animationForChild(child)
- .translationY(fromY, p.y)
- .start();
- }
- updateBubblePositions();
+ return;
}
+
+ if (mPositioner.showBubblesVertically()) {
+ float fromX = onLeft
+ ? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
+ : p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationX(fromX, p.y)
+ .start();
+ } else {
+ float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationY(fromY, p.y)
+ .start();
+ }
+ updateBubblePositions();
}
}
@@ -572,7 +558,6 @@
if (mAnimatingExpand || mAnimatingCollapse) {
return;
}
- boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
for (int i = 0; i < mLayout.getChildCount(); i++) {
final View bubble = mLayout.getChildAt(i);
@@ -582,7 +567,7 @@
return;
}
- final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(i, mBubbleStackView.getState());
animationForChild(bubble)
.translationX(p.x)
.translationY(p.y)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 9a08190..60b6433 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -127,9 +127,6 @@
/** Whether or not the stack's start position has been set. */
private boolean mStackMovedToStartPosition = false;
- /** The height of the most recently visible IME. */
- private float mImeHeight = 0f;
-
/**
* The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
* IME is not visible or the user moved the stack since the IME became visible.
@@ -173,7 +170,7 @@
*/
private boolean mSpringToTouchOnNextMotionEvent = false;
- /** Horizontal offset of bubbles in the stack. */
+ /** Offset of bubbles in the stack (i.e. how much they overlap). */
private float mStackOffset;
/** Offset between stack y and animation y for bubble swap. */
private float mSwapAnimationOffset;
@@ -521,16 +518,6 @@
removeEndActionForProperty(DynamicAnimation.TRANSLATION_Y);
}
- /** Save the current IME height so that we know where the stack bounds should be. */
- public void setImeHeight(int imeHeight) {
- mImeHeight = imeHeight;
- }
-
- /** Returns the current IME height that the stack is offset by. */
- public float getImeHeight() {
- return mImeHeight;
- }
-
/**
* Animates the stack either away from the newly visible IME, or back to its original position
* due to the IME going away.
@@ -589,11 +576,14 @@
*/
public RectF getAllowableStackPositionRegion() {
final RectF allowableRegion = new RectF(mPositioner.getAvailableRect());
+ final int imeHeight = mPositioner.getImeHeight();
+ final float bottomPadding = getBubbleCount() > 1
+ ? mBubblePaddingTop + mStackOffset
+ : mBubblePaddingTop;
allowableRegion.left -= mBubbleOffscreen;
allowableRegion.top += mBubblePaddingTop;
allowableRegion.right += mBubbleOffscreen - mBubbleSize;
- allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize
- + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f);
+ allowableRegion.bottom -= imeHeight + bottomPadding + mBubbleSize;
return allowableRegion;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 754b8da..27c8d7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -125,8 +125,8 @@
mRotation = configuration.windowConfiguration.getRotation();
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
- mSplitWindowManager = new SplitWindowManager(
- windowName, mContext, configuration, parentContainerCallbacks);
+ mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration,
+ parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
mDismissingParallaxPolicy = new DismissingParallaxPolicy();
@@ -181,22 +181,19 @@
public boolean updateConfiguration(Configuration configuration) {
boolean affectsLayout = false;
- // Make sure to render the divider bar with proper resources that matching the screen
- // orientation.
- final int orientation = configuration.orientation;
- if (orientation != mOrientation) {
- mOrientation = orientation;
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- affectsLayout = true;
- }
-
// Update the split bounds when necessary. Besides root bounds changed, split bounds need to
// be updated when the rotation changed to cover the case that users rotated the screen 180
// degrees.
+ // Make sure to render the divider bar with proper resources that matching the screen
+ // orientation.
final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ final int orientation = configuration.orientation;
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)
+ || orientation != mOrientation) {
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ mOrientation = orientation;
mTempRect.set(mRootBounds);
mRootBounds.set(rootBounds);
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index fc7edfc..47dceb3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -64,6 +64,7 @@
public interface ParentContainerCallbacks {
void attachToParentSurface(SurfaceControl.Builder b);
+ void onLeashReady(SurfaceControl leash);
}
public SplitWindowManager(String windowName, Context context, Configuration config,
@@ -100,6 +101,7 @@
.setCallsite("SplitWindowManager#attachToParentSurface");
mParentContainerCallbacks.attachToParentSurface(builder);
mLeash = builder.build();
+ mParentContainerCallbacks.onLeashReady(mLeash);
b.setParent(mLeash);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 954ca14..e511bff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -445,6 +445,9 @@
mOneHandedSettingsUtil.registerSettingsKeyObserver(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
mContext.getContentResolver(), mShortcutEnabledObserver, newUserId);
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ mContext.getContentResolver(), mShortcutEnabledObserver, newUserId);
}
private void unregisterSettingObservers() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d9708f0..493870d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,8 +18,6 @@
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -90,6 +88,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -163,6 +162,19 @@
mDismissTop = NO_DISMISS;
};
+ private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+ new SplitWindowManager.ParentContainerCallbacks() {
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
+ }
+
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
+ };
+
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
@@ -501,7 +513,8 @@
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
- // Reset divider position.
+ // Hide divider and reset its position.
+ setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (childrenToTop != null) {
@@ -635,10 +648,16 @@
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
final boolean sideStageVisible = mSideStageListener.mVisible;
final boolean mainStageVisible = mMainStageListener.mVisible;
- // Divider is only visible if both the main stage and side stages are visible
- setDividerVisibility(isSplitScreenVisible());
+ final boolean bothStageVisible = sideStageVisible && mainStageVisible;
+ final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
+ final boolean sameVisibility = sideStageVisible == mainStageVisible;
+ // Only add or remove divider when both visible or both invisible to avoid sometimes we only
+ // got one stage visibility changed for a moment and it will cause flicker.
+ if (sameVisibility) {
+ setDividerVisibility(bothStageVisible);
+ }
- if (!mainStageVisible && !sideStageVisible) {
+ if (bothStageInvisible) {
if (mExitSplitScreenOnHide
// Don't dismiss staged split when both stages are not visible due to sleeping display,
// like the cases keyguard showing or screen off.
@@ -655,59 +674,32 @@
exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
}
- // When both stage's visibility changed to visible, main stage might receives visibility
- // changed before side stage if it has higher z-order than side stage. Make sure we only
- // update main stage's windowing mode with the visibility changed of side stage to prevent
- // stacking multiple windowing mode transactions which result to flicker issue.
- if (mainStageVisible && stageListener == mSideStageListener) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (sideStageVisible) {
- // The main stage configuration should to follow split layout when side stage is
- // visible.
- mMainStage.updateConfiguration(
- WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
- } else if (!mSideStage.mRootTaskInfo.isSleeping) {
- // We want the main stage configuration to be fullscreen when the side stage isn't
- // visible.
- // We should not do it when side stage are not visible due to sleeping display too.
- mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
- }
- // TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
- mTaskOrganizer.applyTransaction(wct);
- }
-
mSyncQueue.runInSync(t -> {
final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
final SurfaceControl mainStageLeash = mMainStage.mRootLeash;
if (sideStageVisible) {
final Rect sideStageBounds = getSideStageBounds();
- t.show(sideStageLeash)
- .setPosition(sideStageLeash,
- sideStageBounds.left, sideStageBounds.top)
+ t.setPosition(sideStageLeash,
+ sideStageBounds.left, sideStageBounds.top)
.setWindowCrop(sideStageLeash,
sideStageBounds.width(), sideStageBounds.height());
- } else {
- t.hide(sideStageLeash);
}
if (mainStageVisible) {
final Rect mainStageBounds = getMainStageBounds();
- t.show(mainStageLeash);
- if (sideStageVisible) {
- t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
- .setWindowCrop(mainStageLeash,
- mainStageBounds.width(), mainStageBounds.height());
- } else {
- // Clear window crop and position if side stage isn't visible.
- t.setPosition(mainStageLeash, 0, 0)
- .setWindowCrop(mainStageLeash, null);
- }
- } else {
- t.hide(mainStageLeash);
+ t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
+ .setWindowCrop(mainStageLeash,
+ mainStageBounds.width(), mainStageBounds.height());
}
- applyDividerVisibility(t);
+ // Same above, we only set root tasks and divider leash visibility when both stage
+ // change to visible or invisible to avoid flicker.
+ if (sameVisibility) {
+ t.setVisibility(sideStageLeash, bothStageVisible)
+ .setVisibility(mainStageLeash, bothStageVisible);
+ applyDividerVisibility(t);
+ }
});
}
@@ -726,7 +718,6 @@
} else {
t.hide(dividerLeash);
}
-
}
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
@@ -852,8 +843,7 @@
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
- mDisplayAreaInfo.configuration, this,
- b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
+ mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
mDisplayImeController, mTaskOrganizer);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
@@ -871,7 +861,6 @@
&& mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
&& mMainStage.isActive()) {
onLayoutChanged(mSplitLayout);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index debe6d5..ff3428c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -559,6 +559,15 @@
removeWindowSynced(taskId, null, null, false);
}
+ void onImeDrawnOnTask(int taskId) {
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null && record.mTaskSnapshotWindow != null
+ && record.mTaskSnapshotWindow.hasImeSurface()) {
+ record.mTaskSnapshotWindow.removeImmediately();
+ }
+ mStartingWindowRecords.remove(taskId);
+ }
+
protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
@@ -586,14 +595,15 @@
Slog.e(TAG, "Found empty splash screen, remove!");
removeWindowInner(record.mDecorView, false);
}
+ mStartingWindowRecords.remove(taskId);
}
if (record.mTaskSnapshotWindow != null) {
if (DEBUG_TASK_SNAPSHOT) {
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
- record.mTaskSnapshotWindow.remove();
+ record.mTaskSnapshotWindow.scheduleRemove(
+ () -> mStartingWindowRecords.remove(taskId));
}
- mStartingWindowRecords.remove(taskId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a5c47c4..4433e27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -134,7 +134,7 @@
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
suggestionType);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ final TaskSnapshot snapshot = windowInfo.taskSnapshot;
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
snapshot);
}
@@ -177,6 +177,13 @@
}
/**
+ * Called when the IME has drawn on the organized task.
+ */
+ public void onImeDrawnOnTask(int taskId) {
+ mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.onImeDrawnOnTask(taskId));
+ }
+
+ /**
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index cdc0795..3538fd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.startingsurface;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -64,7 +63,6 @@
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
@@ -72,7 +70,6 @@
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -120,7 +117,12 @@
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
- private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350;
+ /**
+ * The max delay time in milliseconds for removing the task snapshot window with IME visible.
+ * Ideally the delay time will be shorter when receiving
+ * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
+ */
+ private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 450;
//tmp vars for unused relayout params
private static final Point TMP_SURFACE_SIZE = new Point();
@@ -139,7 +141,6 @@
private final RectF mTmpDstFrame = new RectF();
private final CharSequence mTitle;
private boolean mHasDrawn;
- private long mShownTime;
private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
private final int mActivityType;
@@ -149,6 +150,8 @@
private final SurfaceControl.Transaction mTransaction;
private final Matrix mSnapshotMatrix = new Matrix();
private final float[] mTmpFloat9 = new float[9];
+ private Runnable mScheduledRunnable;
+ private final boolean mHasImeSurface;
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
TaskSnapshot snapshot, ShellExecutor splashScreenExecutor,
@@ -217,7 +220,7 @@
taskDescription.setBackgroundColor(WHITE);
}
- final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE
+ final long delayRemovalTime = snapshot.hasImeSurface() ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
: DELAY_REMOVAL_TIME_GENERAL;
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
@@ -227,13 +230,12 @@
final Window window = snapshotSurface.mWindow;
final InsetsState tmpInsetsState = new InsetsState();
- final InsetsVisibilities tmpRequestedVisibilities = new InsetsVisibilities();
final InputChannel tmpInputChannel = new InputChannel();
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- tmpRequestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
+ info.requestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
@@ -283,12 +285,17 @@
mDelayRemovalTime = delayRemovalTime;
mTransaction = new SurfaceControl.Transaction();
mClearWindowHandler = clearWindowHandler;
+ mHasImeSurface = snapshot.hasImeSurface();
}
int getBackgroundColor() {
return mBackgroundPaint.getColor();
}
+ boolean hasImeSurface() {
+ return mHasImeSurface;
+ }
+
/**
* Ask system bar background painter to draw status bar background.
* @hide
@@ -306,21 +313,26 @@
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
- void remove() {
- final long now = SystemClock.uptimeMillis();
- if ((now - mShownTime < mDelayRemovalTime)
- // Show the latest content as soon as possible for unlocking to home.
- && mActivityType != ACTIVITY_TYPE_HOME) {
- final long delayTime = mShownTime + mDelayRemovalTime - now;
- mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime);
- if (DEBUG) {
- Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
- }
- return;
+ void scheduleRemove(Runnable onRemove) {
+ if (mScheduledRunnable != null) {
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
+ mScheduledRunnable = null;
}
+ mScheduledRunnable = () -> {
+ TaskSnapshotWindow.this.removeImmediately();
+ onRemove.run();
+ };
+ mSplashScreenExecutor.executeDelayed(mScheduledRunnable, mDelayRemovalTime);
+ if (DEBUG) {
+ Slog.d(TAG, "Defer removing snapshot surface in " + mDelayRemovalTime);
+ }
+ }
+
+ void removeImmediately() {
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
try {
if (DEBUG) {
- Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
+ Slog.d(TAG, "Removing taskSnapshot surface, mHasDrawn: " + mHasDrawn);
}
mSession.remove(mWindow);
} catch (RemoteException e) {
@@ -358,7 +370,6 @@
} else {
drawSizeMatchSnapshot();
}
- mShownTime = SystemClock.uptimeMillis();
mHasDrawn = true;
reportDrawn();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 848eff4..e6d6028 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -107,7 +107,7 @@
* rotation must be the same).
*/
private boolean isSnapshotCompatible(StartingWindowInfo windowInfo) {
- final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ final TaskSnapshot snapshot = windowInfo.taskSnapshot;
if (snapshot == null) {
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index b36468b..e736dce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -16,11 +16,11 @@
package com.android.wm.shell.flicker
-import android.content.ComponentName
import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
@@ -72,7 +72,7 @@
fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -83,7 +83,7 @@
fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
@@ -94,7 +94,7 @@
fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -105,7 +105,7 @@
fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index ff1a6e6..40891f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -17,8 +17,8 @@
@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-import android.content.ComponentName
+import com.android.server.wm.traces.common.FlickerComponentName
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#")
\ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index cdf89a5..c1ec324 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -38,6 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -55,7 +56,7 @@
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5a438af..623055f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,15 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.graphics.Region
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
- component: ComponentName
+ component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index f15044e..57bc0d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.os.SystemProperties
@@ -28,13 +27,13 @@
import androidx.test.uiautomator.Until
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.common.FlickerComponentName
import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
launcherName: String,
- component: ComponentName
+ component: FlickerComponentName
) : StandardAppHelper(
instrumentation,
launcherName,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
index b4ae187..471e010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class FixedAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.FixedActivity.LABEL,
- Components.FixedActivity.COMPONENT
+ Components.FixedActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 086e8b7..0f00ede 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -21,13 +21,14 @@
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.testapp.Components
open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT
+ Components.ImeActivity.COMPONENT.toFlickerComponent()
) {
/**
* Opens the IME and wait for it to be displayed
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
index 7f99e62..12ccbaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -17,14 +17,14 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.provider.Settings
+import com.android.server.wm.traces.common.FlickerComponentName
class MultiWindowHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 7a17bb3..2357b0d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -26,6 +26,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
@@ -34,7 +35,7 @@
class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.PipActivity.LABEL,
- Components.PipActivity.COMPONENT
+ Components.PipActivity.COMPONENT.toFlickerComponent()
) {
private val mediaSessionManager: MediaSessionManager
get() = context.getSystemService(MediaSessionManager::class.java)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
index ba13e38..4d0fbc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SimpleAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.SimpleActivity.LABEL,
- Components.SimpleActivity.COMPONENT
+ Components.SimpleActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 2d996ca..0ec9b2d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -17,14 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.res.Resources
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
@@ -39,16 +40,16 @@
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT)
+ Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent())
fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT)
+ Components.NonResizeableActivity.COMPONENT.toFlickerComponent())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 18e0cc9e..cad38ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -31,7 +30,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
- splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
+ splitScreenApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, LAUNCHER_COMPONENT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 6080912..b60cbcf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -64,10 +63,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
splitScreenApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 1dfc59e..94ae9da 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ secondaryApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index a278704..ee6cf34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -71,10 +70,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
nonResizeableApp.component,
splitScreenApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 1b8afa6..2beb8a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -68,12 +67,12 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- nonResizeableApp.component,
- splitScreenApp.component)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 247965f..2b629b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index ff34364..95fe3be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 95e4085..6bc717a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ nonResizeableApp.component, splitScreenApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 65346aa..212acc7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 547341a..6912960 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -28,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -71,11 +70,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 3f86658..b7a78ce 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -27,7 +26,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -70,11 +69,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 5fb6f1f..b6680d9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -39,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -86,9 +85,9 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 3117693..661c8b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -32,7 +31,7 @@
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
@@ -50,7 +49,7 @@
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_COMPONENT = ComponentName("",
+ protected val LAUNCHER_COMPONENT = FlickerComponentName("",
LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage)
private var prevDevEnableNonResizableMultiWindow = 0
@@ -79,9 +78,9 @@
*
* b/182720234
*/
- open val ignoredWindows: List<ComponentName> = listOf(
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ open val ignoredWindows: List<FlickerComponentName> = listOf(
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -148,9 +147,9 @@
}
companion object {
- internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+ internal val LIVE_WALLPAPER_COMPONENT = FlickerComponentName("",
"com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
- internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
- internal val TOAST_COMPONENT = ComponentName("", "Toast")
+ internal val LETTERBOX_COMPONENT = FlickerComponentName("", "Letterbox")
+ internal val TOAST_COMPONENT = FlickerComponentName("", "Toast")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index a7ac6a7..34eff80 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -29,7 +28,7 @@
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -59,10 +58,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index cd15051..14b006e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -42,6 +42,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.testapp.Components
@@ -110,7 +111,7 @@
@Test
fun topAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
+ this.isAppWindowVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@@ -118,7 +119,7 @@
@Test
fun bottomAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
+ this.isAppWindowVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -142,14 +143,14 @@
@Test
fun topAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.SimpleActivity.COMPONENT)
+ this.isVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@Test
fun bottomAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.ImeActivity.COMPONENT)
+ this.isVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -174,8 +175,10 @@
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
@@ -194,8 +197,10 @@
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 046972d..52a744f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,7 +26,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 097ccb8..d17a2bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -29,7 +29,7 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -106,7 +106,7 @@
}
/**
- * Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.NAV_BAR] has the correct position at
* the start and end of the transition
*/
@FlakyTest
@@ -115,7 +115,7 @@
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
/**
- * Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
* the start and end of the transition
*/
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index faaaecb..8d21d83 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.toLayerName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index fa100b5..b53342d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 617ef22..1fec3cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 89b2c40..ce840ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index ed04fc9..6e0324c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -20,8 +20,6 @@
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.traces.RegionSubject
-import com.android.server.wm.traces.parser.toLayerName
-import com.android.server.wm.traces.parser.toWindowName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 9dad336..aba8ace 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -90,7 +90,7 @@
@Test
fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
+ isAboveWindow(FlickerComponentName.IME, pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 9732a88..2b9bdce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -16,10 +16,12 @@
package com.android.wm.shell.bubbles.animation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
@@ -37,6 +39,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
import org.junit.Before;
import org.junit.Ignore;
@@ -56,18 +59,22 @@
private int mStackOffset;
private PointF mExpansionPoint;
private BubblePositioner mPositioner;
+ private BubbleStackView.StackViewState mStackViewState;
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
+ BubbleStackView stackView = mock(BubbleStackView.class);
+ when(stackView.getState()).thenReturn(getStackViewState());
mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
mExpandedController = new ExpandedAnimationController(mPositioner,
- mOnBubbleAnimatedOutAction);
+ mOnBubbleAnimatedOutAction,
+ stackView);
spyOn(mExpandedController);
addOneMoreThanBubbleLimitBubbles();
@@ -78,6 +85,13 @@
mExpansionPoint = new PointF(100, 100);
}
+ public BubbleStackView.StackViewState getStackViewState() {
+ mStackViewState.numberOfBubbles = mLayout.getChildCount();
+ mStackViewState.selectedIndex = 0;
+ mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
+ return mStackViewState;
+ }
+
@Test
@Ignore
public void testExpansionAndCollapse() throws InterruptedException {
@@ -141,12 +155,10 @@
/** Check that children are in the correct positions for being expanded. */
private void testBubblesInCorrectExpandedPositions() {
- boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
// Check all the visible bubbles to see if they're in the right place.
for (int i = 0; i < mLayout.getChildCount(); i++) {
PointF expectedPosition = mPositioner.getExpandedBubbleXY(i,
- mLayout.getChildCount(),
- onLeft);
+ getStackViewState());
assertEquals(expectedPosition.x,
mLayout.getChildAt(i).getTranslationX(),
2f);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 3557906..defa58d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -29,7 +29,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.view.SurfaceControl;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -53,7 +52,7 @@
@RunWith(AndroidJUnit4.class)
public class SplitLayoutTests extends ShellTestCase {
@Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
- @Mock SurfaceControl mRootLeash;
+ @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
@@ -67,7 +66,7 @@
mContext,
getConfiguration(),
mSplitLayoutHandler,
- b -> b.setParent(mRootLeash),
+ mCallbacks,
mDisplayImeController,
mTaskOrganizer));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index c456c7d..9bb54a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -23,7 +23,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.InsetsState;
-import android.view.SurfaceControl;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -41,8 +40,8 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SplitWindowManagerTests extends ShellTestCase {
- @Mock SurfaceControl mSurfaceControl;
@Mock SplitLayout mSplitLayout;
+ @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
private SplitWindowManager mSplitWindowManager;
@Before
@@ -51,7 +50,7 @@
final Configuration configuration = new Configuration();
configuration.setToDefaults();
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
- b -> b.setParent(mSurfaceControl));
+ mCallbacks);
when(mSplitLayout.getDividerBounds()).thenReturn(
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
configuration.windowConfiguration.getBounds().height()));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 2994e71..36722d9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -15,28 +15,38 @@
*/
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -44,11 +54,16 @@
import android.os.UserHandle;
import android.testing.TestableContext;
import android.view.Display;
+import android.view.IWindowSession;
+import android.view.InsetsState;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
import android.window.StartingWindowInfo;
+import android.window.TaskSnapshot;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -63,6 +78,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.function.IntSupplier;
@@ -80,6 +96,7 @@
private TransactionPool mTransactionPool;
private final Handler mTestHandler = new Handler(Looper.getMainLooper());
+ private ShellExecutor mTestExecutor;
private final TestableContext mTestContext = new TestContext(
InstrumentationRegistry.getInstrumentation().getTargetContext());
TestStartingSurfaceDrawer mStartingSurfaceDrawer;
@@ -138,9 +155,9 @@
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
doNothing().when(mMockWindowManager).addView(any(), any());
-
- mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(mTestContext,
- new HandlerExecutor(mTestHandler), mTransactionPool));
+ mTestExecutor = new HandlerExecutor(mTestHandler);
+ mStartingSurfaceDrawer = spy(
+ new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mTransactionPool));
}
@Test
@@ -208,6 +225,48 @@
assertEquals(0, windowColor3.mReuseCount);
}
+ @Test
+ public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception {
+ final int taskId = 1;
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, android.R.style.Theme);
+ TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100),
+ new Rect(0, 0, 0, 50), true /* hasImeSurface */);
+ final IWindowSession session = WindowManagerGlobal.getWindowSession();
+ spyOn(session);
+ doReturn(WindowManagerGlobal.ADD_OKAY).when(session).addToDisplay(
+ any() /* window */, any() /* attrs */,
+ anyInt() /* viewVisibility */, anyInt() /* displayId */,
+ any() /* requestedVisibility */, any() /* outInputChannel */,
+ any() /* outInsetsState */, any() /* outActiveControls */);
+ TaskSnapshotWindow mockSnapshotWindow = TaskSnapshotWindow.create(windowInfo,
+ mBinder,
+ snapshot, mTestExecutor, () -> {
+ });
+ spyOn(mockSnapshotWindow);
+ try (AutoCloseable mockTaskSnapshotSession = new AutoCloseable() {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(TaskSnapshotWindow.class)
+ .startMocking();
+ @Override
+ public void close() {
+ mockSession.finishMocking();
+ }
+ }) {
+ when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
+ any())).thenReturn(mockSnapshotWindow);
+ // Simulate a task snapshot window created with IME snapshot shown.
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot);
+ waitHandlerIdle(mTestHandler);
+
+ // Verify the task snapshot with IME snapshot will be removed when received the real IME
+ // drawn callback.
+ mStartingSurfaceDrawer.onImeDrawnOnTask(1);
+ verify(mockSnapshotWindow).removeImmediately();
+ }
+ }
+
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
@@ -219,10 +278,27 @@
taskInfo.taskId = taskId;
windowInfo.targetActivityInfo = info;
windowInfo.taskInfo = taskInfo;
+ windowInfo.topOpaqueWindowInsetsState = new InsetsState();
+ windowInfo.mainWindowLayoutParams = new WindowManager.LayoutParams();
+ windowInfo.topOpaqueWindowLayoutParams = new WindowManager.LayoutParams();
return windowInfo;
}
private static void waitHandlerIdle(Handler handler) {
handler.runWithScissors(() -> { }, 0 /* timeout */);
}
+
+ private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
+ Rect contentInsets, boolean hasImeSurface) {
+ final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
+ 1, HardwareBuffer.USAGE_CPU_READ_RARELY);
+ return new TaskSnapshot(
+ System.currentTimeMillis(),
+ new ComponentName("", ""), buffer,
+ ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+ Surface.ROTATION_0, taskSize, contentInsets, false,
+ true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ 0 /* systemUiVisibility */, false /* isTranslucent */,
+ hasImeSurface /* hasImeSurface */);
+ }
}
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index 498ec57..3ea4951 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -45,7 +45,10 @@
* WARNING! This observer may not receive metrics for the last several frames that the app
* produces.
*/
- FrameMetricsObserver(bool waitForPresentTime) : mWaitForPresentTime(waitForPresentTime) {}
+ FrameMetricsObserver(bool waitForPresentTime)
+ : mWaitForPresentTime(waitForPresentTime)
+ , mSurfaceControlId(INT32_MAX)
+ , mAttachedFrameNumber(UINT64_MAX) {}
private:
const bool mWaitForPresentTime;
diff --git a/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
index fb04700..571a267 100644
--- a/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
+++ b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
@@ -34,11 +34,13 @@
MOCK_METHOD(void, notify, (const int64_t* buffer), (override));
};
-TEST(FrameMetricsReporter, reportsAllFramesIfNoFromFrameIsSpecified) {
+// To make sure it is clear that something went wrong if no from frame is set (to make it easier
+// to catch bugs were we forget to set the fromFrame).
+TEST(FrameMetricsReporter, doesNotReportAnyFrameIfNoFromFrameIsSpecified) {
auto reporter = std::make_shared<FrameMetricsReporter>();
auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
- EXPECT_CALL(*observer, notify).Times(4);
+ EXPECT_CALL(*observer, notify).Times(0);
reporter->addObserver(observer.get());
@@ -62,17 +64,19 @@
}
TEST(FrameMetricsReporter, respectsWaitForPresentTimeUnset) {
- auto reporter = std::make_shared<FrameMetricsReporter>();
-
- auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
- reporter->addObserver(observer.get());
-
const int64_t* stats;
bool hasPresentTime = false;
uint64_t frameNumber = 3;
int32_t surfaceControlId = 0;
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
EXPECT_CALL(*observer, notify).Times(1);
+ hasPresentTime = false;
reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
EXPECT_CALL(*observer, notify).Times(0);
@@ -81,17 +85,19 @@
}
TEST(FrameMetricsReporter, respectsWaitForPresentTimeSet) {
- auto reporter = std::make_shared<FrameMetricsReporter>();
-
- auto observer = sp<TestFrameMetricsObserver>::make(true /*waitForPresentTime*/);
- reporter->addObserver(observer.get());
-
const int64_t* stats;
- bool hasPresentTime = false;
+ bool hasPresentTime = true;
uint64_t frameNumber = 3;
int32_t surfaceControlId = 0;
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
EXPECT_CALL(*observer, notify).Times(0);
+ hasPresentTime = false;
reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
EXPECT_CALL(*observer, notify).Times(1);
diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java
index 6633d24..ce73be1 100644
--- a/location/java/android/location/GnssAntennaInfo.java
+++ b/location/java/android/location/GnssAntennaInfo.java
@@ -30,6 +30,9 @@
* A class that contains information about a GNSS antenna. GNSS antenna characteristics can change
* with device configuration, such as when a device is folded open or closed. Antenna information is
* delivered to registered instances of {@link Listener}.
+ *
+ * <p> Antenna info parameters are measured for each specific device model by the device
+ * manufacturers and provided to the Android framework.
*/
public final class GnssAntennaInfo implements Parcelable {
private final double mCarrierFrequencyMHz;
diff --git a/media/Android.bp b/media/Android.bp
index 7592c15..b049cb6 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -56,6 +56,10 @@
"aidl/android/media/audio/common/AudioConfig.aidl",
"aidl/android/media/audio/common/AudioConfigBase.aidl",
"aidl/android/media/audio/common/AudioContentType.aidl",
+ "aidl/android/media/audio/common/AudioDevice.aidl",
+ "aidl/android/media/audio/common/AudioDeviceAddress.aidl",
+ "aidl/android/media/audio/common/AudioDeviceDescription.aidl",
+ "aidl/android/media/audio/common/AudioDeviceType.aidl",
"aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl",
"aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
"aidl/android/media/audio/common/AudioEncapsulationType.aidl",
diff --git a/media/aidl/android/media/audio/common/AudioDevice.aidl b/media/aidl/android/media/audio/common/AudioDevice.aidl
new file mode 100644
index 0000000..f897eb2
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDevice.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioDeviceAddress;
+import android.media.audio.common.AudioDeviceDescription;
+
+/**
+ * Represents a concrete audio device by bundling together the device type and
+ * the device address.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioDevice {
+ AudioDeviceDescription type;
+ AudioDeviceAddress address;
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl b/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl
new file mode 100644
index 0000000..ca48f7e
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceAddress.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+/**
+ * This structure defines various representations for the audio device
+ * address.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+union AudioDeviceAddress {
+ /**
+ * String uniquely identifying the device among other devices
+ * of the same type. Can be empty in case there is only one device
+ * of this type.
+ *
+ * Depending on the device type, its id may be assigned by the framework
+ * (one case used at the time of writing is REMOTE_SUBMIX), or assigned by
+ * the HAL service (the canonical examples are BUS and MIC devices). In any
+ * case, both framework and HAL must never attempt to parse the value of the
+ * 'id' field, regardless of whom has generated it. If the address must be
+ * parsed, one of the members below must be used instead of 'id'.
+ */
+ @utf8InCpp String id;
+ /**
+ * IEEE 802 MAC address. Set for Bluetooth devices. The array must have
+ * exactly 6 elements.
+ */
+ byte[] mac;
+ /**
+ * IPv4 Address. Set for IPv4 devices. The array must have exactly 4
+ * elements.
+ */
+ byte[] ipv4;
+ /**
+ * IPv6 Address. Set for IPv6 devices. The array must have exactly 8
+ * elements.
+ */
+ int[] ipv6;
+ /**
+ * PCI bus Address. Set for USB devices. The array must have exactly 2
+ * elements, in the following order: the card id, and the device id.
+ */
+ int[] alsa;
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl b/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl
new file mode 100644
index 0000000..4e8a735
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceDescription.aidl
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioDeviceType;
+
+/**
+ * Describes the kind of an audio device.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioDeviceDescription {
+ /**
+ * Type and directionality of the device. For bidirectional audio devices
+ * two descriptions need to be created, having the same value for
+ * the 'connection' field.
+ *
+ * See 'AudioDeviceType' for the list of supported values.
+ */
+ AudioDeviceType type = AudioDeviceType.NONE;
+ /**
+ * Specifies the type of the connection of the device to the audio system.
+ * Usually it's some kind of a communication protocol, e.g. Bluetooth SCO or
+ * USB. There is a list of connection types recognized by the framework,
+ * defined using 'CONNECTION_' constants. Vendors can add their own
+ * connection types with "vx.<vendor>." prefix.
+ *
+ * When the 'connection' field is left empty and 'type != NONE | DEFAULT',
+ * it is assumed that the device is permanently attached to the audio
+ * system, e.g. a built-in speaker or microphone.
+ *
+ * The 'connection' field must be left empty if 'type' is 'NONE' or
+ * '{IN|OUT}_DEFAULT'.
+ */
+ @utf8InCpp String connection;
+ /**
+ * Analog connection, for example, via 3.5 mm analog jack.
+ */
+ const @utf8InCpp String CONNECTION_ANALOG = "analog";
+ /**
+ * Low-End (Analog) Desk Dock.
+ */
+ const @utf8InCpp String CONNECTION_ANALOG_DOCK = "analog-dock";
+ /**
+ * Bluetooth A2DP connection.
+ */
+ const @utf8InCpp String CONNECTION_BT_A2DP = "bt-a2dp";
+ /**
+ * Bluetooth Low Energy (LE) connection.
+ */
+ const @utf8InCpp String CONNECTION_BT_LE = "bt-le";
+ /**
+ * Bluetooth SCO connection.
+ */
+ const @utf8InCpp String CONNECTION_BT_SCO = "bt-sco";
+ /**
+ * Bus connection. Mostly used in automotive scenarios.
+ */
+ const @utf8InCpp String CONNECTION_BUS = "bus";
+ /**
+ * High-End (Digital) Desk Dock.
+ */
+ const @utf8InCpp String CONNECTION_DIGITAL_DOCK = "digital-dock";
+ /**
+ * HDMI connection.
+ */
+ const @utf8InCpp String CONNECTION_HDMI = "hdmi";
+ /**
+ * HDMI ARC connection.
+ */
+ const @utf8InCpp String CONNECTION_HDMI_ARC = "hdmi-arc";
+ /**
+ * HDMI eARC connection.
+ */
+ const @utf8InCpp String CONNECTION_HDMI_EARC = "hdmi-earc";
+ /**
+ * IP v4 connection.
+ */
+ const @utf8InCpp String CONNECTION_IP_V4 = "ip-v4";
+ /**
+ * SPDIF connection.
+ */
+ const @utf8InCpp String CONNECTION_SPDIF = "spdif";
+ /**
+ * A wireless connection when the actual protocol is unspecified.
+ */
+ const @utf8InCpp String CONNECTION_WIRELESS = "wireless";
+ /**
+ * USB connection.
+ */
+ const @utf8InCpp String CONNECTION_USB = "usb";
+}
diff --git a/media/aidl/android/media/audio/common/AudioDeviceType.aidl b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
new file mode 100644
index 0000000..95dbe2a
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioDeviceType.aidl
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.audio.common;
+
+/**
+ * The type of the audio device. Only used as part of 'AudioDeviceDescription'
+ * structure.
+ *
+ * Types are divided into "input" and "output" categories. Audio devices that
+ * have both audio input and output, for example, headsets, are represented by a
+ * pair of input and output device types.
+ *
+ * The 'AudioDeviceType' intentionally binds together directionality and 'kind'
+ * of the device to avoid making them fully orthogonal. This is because not all
+ * types of devices are bidirectional, for example, speakers can only be used
+ * for output and microphones can only be used for input (at least, in the
+ * context of the audio framework).
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="int")
+enum AudioDeviceType {
+ /**
+ * "None" type is a "null" value. All fields of 'AudioDeviceDescription'
+ * must have default / empty / null values.
+ */
+ NONE = 0,
+ /**
+ * The "default" device is used when the client does not have any
+ * preference for a particular device.
+ */
+ IN_DEFAULT = 1,
+ /**
+ * A device implementing Android Open Accessory protocol.
+ */
+ IN_ACCESSORY = 2,
+ /**
+ * Input from a DSP front-end proxy device.
+ */
+ IN_AFE_PROXY = 3,
+ /**
+ * Used when only the connection protocol is known, e.g. a "HDMI Device."
+ */
+ IN_DEVICE = 4,
+ /**
+ * A device providing reference input for echo canceller.
+ */
+ IN_ECHO_REFERENCE = 5,
+ /**
+ * FM Tuner input.
+ */
+ IN_FM_TUNER = 6,
+ /**
+ * A microphone of a headset.
+ */
+ IN_HEADSET = 7,
+ /**
+ * Loopback input.
+ */
+ IN_LOOPBACK = 8,
+ /**
+ * The main microphone (the frontal mic on mobile devices).
+ */
+ IN_MICROPHONE = 9,
+ /**
+ * The secondary microphone (the back mic on mobile devices).
+ */
+ IN_MICROPHONE_BACK = 10,
+ /**
+ * Input from a submix of other streams.
+ */
+ IN_SUBMIX = 11,
+ /**
+ * Audio received via the telephone line.
+ */
+ IN_TELEPHONY_RX = 12,
+ /**
+ * TV Tuner audio input.
+ */
+ IN_TV_TUNER = 13,
+ /**
+ * The "default" device is used when the client does not have any
+ * preference for a particular device.
+ */
+ OUT_DEFAULT = 129,
+ /**
+ * A device implementing Android Open Accessory protocol.
+ */
+ OUT_ACCESSORY = 130,
+ /**
+ * Output from a DSP front-end proxy device.
+ */
+ OUT_AFE_PROXY = 131,
+ /**
+ * Car audio system.
+ */
+ OUT_CARKIT = 132,
+ /**
+ * Used when only the connection protocol is known, e.g. a "HDMI Device."
+ */
+ OUT_DEVICE = 133,
+ /**
+ * The echo canceller device.
+ */
+ OUT_ECHO_CANCELLER = 134,
+ /**
+ * The FM Tuner device.
+ */
+ OUT_FM = 135,
+ /**
+ * Headphones.
+ */
+ OUT_HEADPHONE = 136,
+ /**
+ * Headphones of a headset.
+ */
+ OUT_HEADSET = 137,
+ /**
+ * Hearing aid.
+ */
+ OUT_HEARING_AID = 138,
+ /**
+ * Secondary line level output.
+ */
+ OUT_LINE_AUX = 139,
+ /**
+ * The main speaker.
+ */
+ OUT_SPEAKER = 140,
+ /**
+ * The speaker of a mobile device in the case when it is close to the ear.
+ */
+ OUT_SPEAKER_EARPIECE = 141,
+ /**
+ * The main speaker with overload / overheating protection.
+ */
+ OUT_SPEAKER_SAFE = 142,
+ /**
+ * Output into a submix.
+ */
+ OUT_SUBMIX = 143,
+ /**
+ * Output into a telephone line.
+ */
+ OUT_TELEPHONY_TX = 144,
+}
diff --git a/media/aidl/android/media/audio/common/AudioFormatType.aidl b/media/aidl/android/media/audio/common/AudioFormatType.aidl
index 1759401..ea78c7a 100644
--- a/media/aidl/android/media/audio/common/AudioFormatType.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormatType.aidl
@@ -19,6 +19,8 @@
/**
* The type of the audio format. Only used as part of 'AudioFormatDescription'
* structure.
+ *
+ * {@hide}
*/
@VintfStability
@Backing(type="byte")
diff --git a/media/aidl/android/media/audio/common/PcmType.aidl b/media/aidl/android/media/audio/common/PcmType.aidl
index db77769..6e07d9b 100644
--- a/media/aidl/android/media/audio/common/PcmType.aidl
+++ b/media/aidl/android/media/audio/common/PcmType.aidl
@@ -19,6 +19,8 @@
/**
* The type of the encoding used for representing PCM samples. Only used as
* part of 'AudioFormatDescription' structure.
+ *
+ * {@hide}
*/
@VintfStability
@Backing(type="byte")
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl
new file mode 100644
index 0000000..fb5cb62
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDevice.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioDevice {
+ android.media.audio.common.AudioDeviceDescription type;
+ android.media.audio.common.AudioDeviceAddress address;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl
new file mode 100644
index 0000000..905d3aa
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceAddress.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+union AudioDeviceAddress {
+ @utf8InCpp String id;
+ byte[] mac;
+ byte[] ipv4;
+ int[] ipv6;
+ int[] alsa;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl
new file mode 100644
index 0000000..b4d71d7
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceDescription.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioDeviceDescription {
+ android.media.audio.common.AudioDeviceType type = android.media.audio.common.AudioDeviceType.NONE;
+ @utf8InCpp String connection;
+ const @utf8InCpp String CONNECTION_ANALOG = "analog";
+ const @utf8InCpp String CONNECTION_ANALOG_DOCK = "analog-dock";
+ const @utf8InCpp String CONNECTION_BT_A2DP = "bt-a2dp";
+ const @utf8InCpp String CONNECTION_BT_LE = "bt-le";
+ const @utf8InCpp String CONNECTION_BT_SCO = "bt-sco";
+ const @utf8InCpp String CONNECTION_BUS = "bus";
+ const @utf8InCpp String CONNECTION_DIGITAL_DOCK = "digital-dock";
+ const @utf8InCpp String CONNECTION_HDMI = "hdmi";
+ const @utf8InCpp String CONNECTION_HDMI_ARC = "hdmi-arc";
+ const @utf8InCpp String CONNECTION_HDMI_EARC = "hdmi-earc";
+ const @utf8InCpp String CONNECTION_IP_V4 = "ip-v4";
+ const @utf8InCpp String CONNECTION_SPDIF = "spdif";
+ const @utf8InCpp String CONNECTION_WIRELESS = "wireless";
+ const @utf8InCpp String CONNECTION_USB = "usb";
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
new file mode 100644
index 0000000..ffdb778
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioDeviceType.aidl
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioDeviceType {
+ NONE = 0,
+ IN_DEFAULT = 1,
+ IN_ACCESSORY = 2,
+ IN_AFE_PROXY = 3,
+ IN_DEVICE = 4,
+ IN_ECHO_REFERENCE = 5,
+ IN_FM_TUNER = 6,
+ IN_HEADSET = 7,
+ IN_LOOPBACK = 8,
+ IN_MICROPHONE = 9,
+ IN_MICROPHONE_BACK = 10,
+ IN_SUBMIX = 11,
+ IN_TELEPHONY_RX = 12,
+ IN_TV_TUNER = 13,
+ OUT_DEFAULT = 129,
+ OUT_ACCESSORY = 130,
+ OUT_AFE_PROXY = 131,
+ OUT_CARKIT = 132,
+ OUT_DEVICE = 133,
+ OUT_ECHO_CANCELLER = 134,
+ OUT_FM = 135,
+ OUT_HEADPHONE = 136,
+ OUT_HEADSET = 137,
+ OUT_HEARING_AID = 138,
+ OUT_LINE_AUX = 139,
+ OUT_SPEAKER = 140,
+ OUT_SPEAKER_EARPIECE = 141,
+ OUT_SPEAKER_SAFE = 142,
+ OUT_SUBMIX = 143,
+ OUT_TELEPHONY_TX = 144,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
index b94c850..7f55abe 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormatType.aidl
@@ -32,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.media.audio.common;
+/* @hide */
@Backing(type="byte") @VintfStability
enum AudioFormatType {
DEFAULT = 0,
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
index 42c4679..79bfa62 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/PcmType.aidl
@@ -32,6 +32,7 @@
// later when a module using the interface is updated, e.g., Mainline modules.
package android.media.audio.common;
+/* @hide */
@Backing(type="byte") @VintfStability
enum PcmType {
DEFAULT = 0,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 91834fb..669bfc5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2457,17 +2457,11 @@
/**
* Return a handle to the optional platform's {@link Spatializer}
- * @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
- * otherwise.
+ * @return the {@code Spatializer} instance.
+ * @see Spatializer#getImmersiveAudioLevel() to check for the level of support of the effect
+ * on the platform
*/
- public @Nullable Spatializer getSpatializer() {
- int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- try {
- level = getService().getSpatializerImmersiveAudioLevel();
- } catch (Exception e) { /* using NONE */ }
- if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
- return null;
- }
+ public @NonNull Spatializer getSpatializer() {
return new Spatializer(this);
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c3b1bca..9a82ab1 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2281,12 +2281,6 @@
if (size == null || size.getWidth() * size.getHeight() <= 0) {
continue;
}
- if (size.getWidth() > SIZE_RANGE.getUpper()
- || size.getHeight() > SIZE_RANGE.getUpper()) {
- size = new Size(
- Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
- Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
- }
Range<Long> range = Utils.parseLongRange(map.get(key), null);
if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
continue;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index a15529e..458821e 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -928,7 +928,7 @@
private @NonNull List<Bitmap> getFramesAtIndexInternal(
int frameIndex, int numFrames, @Nullable BitmapParams params) {
if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
- throw new IllegalStateException("Does not contail video or image sequences");
+ throw new IllegalStateException("Does not contain video or image sequences");
}
int frameCount = Integer.parseInt(
extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
@@ -1046,7 +1046,7 @@
private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
- throw new IllegalStateException("Does not contail still images");
+ throw new IllegalStateException("Does not contain still images");
}
String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 3ed8b58..d8519b6 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -114,6 +114,7 @@
/** @hide */
@IntDef(flag = false, value = {
+ SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
SPATIALIZER_IMMERSIVE_LEVEL_NONE,
SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL,
})
@@ -121,21 +122,42 @@
public @interface ImmersiveAudioLevel {};
/**
- * @hide
+ * Constant indicating the {@code Spatializer} on this device supports a spatialization
+ * mode that differs from the ones available at this SDK level.
+ * @see #getImmersiveAudioLevel()
+ */
+ public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1;
+
+ /**
* Constant indicating there are no spatialization capabilities supported on this device.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0;
/**
- * @hide
- * Constant indicating the {@link Spatializer} on this device supports multichannel
+ * Constant indicating the {@code Spatializer} on this device supports multichannel
* spatialization.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
/**
+ * Return the level of support for the spatialization feature on this device.
+ * This level of support is independent of whether the {@code Spatializer} is currently
+ * enabled or available and will not change over time.
+ * @return the level of spatialization support
+ * @see #isEnabled()
+ * @see #isAvailable()
+ */
+ public @ImmersiveAudioLevel int getImmersiveAudioLevel() {
+ int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ try {
+ level = mAm.getService().getSpatializerImmersiveAudioLevel();
+ } catch (Exception e) { /* using NONE */ }
+ return level;
+ }
+
+ /**
* @hide
* Enables / disables the spatializer effect.
* Changing the enabled state will trigger the public
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 3a8decd..ada8901 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-
#include <stdio.h>
+#include <unordered_set>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioEffects-JNI"
@@ -61,21 +61,14 @@
struct effect_callback_cookie {
jclass audioEffect_class; // AudioEffect class
jobject audioEffect_ref; // AudioEffect object instance
- };
-
-// ----------------------------------------------------------------------------
-class AudioEffectJniStorage {
- public:
- effect_callback_cookie mCallbackData;
-
- AudioEffectJniStorage() {
- }
-
- ~AudioEffectJniStorage() {
- }
-
+ bool busy;
+ Condition cond;
};
+// ----------------------------------------------------------------------------
+struct AudioEffectJniStorage {
+ effect_callback_cookie mCallbackData{};
+};
jint AudioEffectJni::translateNativeErrorToJava(int code) {
switch(code) {
@@ -104,6 +97,7 @@
}
static Mutex sLock;
+static std::unordered_set<effect_callback_cookie*> sAudioEffectCallBackCookies;
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
@@ -124,7 +118,13 @@
ALOGW("effectCallback error user %p, env %p", user, env);
return;
}
-
+ {
+ Mutex::Autolock l(sLock);
+ if (sAudioEffectCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy = true;
+ }
ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
@@ -191,6 +191,11 @@
env->ExceptionDescribe();
env->ExceptionClear();
}
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy = false;
+ callbackInfo->cond.broadcast();
+ }
}
// ----------------------------------------------------------------------------
@@ -401,6 +406,10 @@
setAudioEffect(env, thiz, lpAudioEffect);
}
+ {
+ Mutex::Autolock l(sLock);
+ sAudioEffectCallBackCookies.insert(&lpJniStorage->mCallbackData);
+ }
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
return (jint) AUDIOEFFECT_SUCCESS;
@@ -432,6 +441,7 @@
// ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
if (lpAudioEffect == 0) {
@@ -447,7 +457,17 @@
env->SetLongField(thiz, fields.fidJniData, 0);
if (lpJniStorage) {
- ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+ Mutex::Autolock l(sLock);
+ effect_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
+ ALOGV("deleting lpJniStorage: %p\n", lpJniStorage);
+ sAudioEffectCallBackCookies.erase(lpCookie);
+ while (lpCookie->busy) {
+ if (lpCookie->cond.waitRelative(sLock,
+ milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+ NO_ERROR) {
+ break;
+ }
+ }
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index b30f00f..7b00f93 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -15,6 +15,7 @@
*/
#include <stdio.h>
+#include <unordered_set>
//#define LOG_NDEBUG 0
#define LOG_TAG "visualizers-JNI"
@@ -68,6 +69,12 @@
jclass visualizer_class; // Visualizer class
jobject visualizer_ref; // Visualizer object instance
+ // 'busy_count' and 'cond' together with 'sLock' are used to serialize
+ // concurrent access to the callback cookie from 'setup'/'release'
+ // and the callback.
+ int busy_count;
+ Condition cond;
+
// Lazily allocated arrays used to hold callback data provided to java
// applications. These arrays are allocated during the first callback and
// reallocated when the size of the callback data changes. Allocating on
@@ -75,14 +82,12 @@
// reference to the provided data (they need to make a copy if they want to
// hold onto outside of the callback scope), but it avoids GC thrash caused
// by constantly allocating and releasing arrays to hold callback data.
+ // 'callback_data_lock' must never be held at the same time with 'sLock'.
Mutex callback_data_lock;
jbyteArray waveform_data;
jbyteArray fft_data;
- visualizer_callback_cookie() {
- waveform_data = NULL;
- fft_data = NULL;
- }
+ // Assumes use of default initialization by the client.
~visualizer_callback_cookie() {
cleanupBuffers();
@@ -107,15 +112,8 @@
};
// ----------------------------------------------------------------------------
-class VisualizerJniStorage {
- public:
- visualizer_callback_cookie mCallbackData;
-
- VisualizerJniStorage() {
- }
-
- ~VisualizerJniStorage() {
- }
+struct VisualizerJniStorage {
+ visualizer_callback_cookie mCallbackData{};
};
@@ -141,6 +139,7 @@
}
static Mutex sLock;
+static std::unordered_set<visualizer_callback_cookie*> sVisualizerCallBackCookies;
// ----------------------------------------------------------------------------
static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -178,11 +177,19 @@
return;
}
+ {
+ Mutex::Autolock l(sLock);
+ if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy_count++;
+ }
ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
callbackInfo,
callbackInfo->visualizer_ref,
callbackInfo->visualizer_class);
+ {
AutoMutex lock(&callbackInfo->callback_data_lock);
if (waveformSize != 0 && waveform != NULL) {
@@ -224,11 +231,17 @@
jArray);
}
}
+ } // callback_data_lock scope
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy_count--;
+ callbackInfo->cond.broadcast();
+ }
}
// ----------------------------------------------------------------------------
@@ -337,16 +350,41 @@
void *info) {
if ((event == AudioEffect::EVENT_ERROR) &&
(*((status_t*)info) == DEAD_OBJECT)) {
- VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
- visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
+ visualizer_callback_cookie* callbackInfo =
+ (visualizer_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (!user || !env) {
+ ALOGW("effectCallback error user %p, env %p", user, env);
+ return;
+ }
+ {
+ Mutex::Autolock l(sLock);
+ if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy_count++;
+ }
+ ALOGV("effectCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
+ callbackInfo,
+ callbackInfo->visualizer_ref,
+ callbackInfo->visualizer_class);
+
env->CallStaticVoidMethod(
callbackInfo->visualizer_class,
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_SERVER_DIED,
0, NULL);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy_count--;
+ callbackInfo->cond.broadcast();
+ }
}
}
@@ -396,7 +434,7 @@
}
lpVisualizer->set(0,
android_media_visualizer_effect_callback,
- lpJniStorage,
+ &lpJniStorage->mCallbackData,
(audio_session_t) sessionId);
lStatus = translateError(lpVisualizer->initCheck());
@@ -417,6 +455,10 @@
setVisualizer(env, thiz, lpVisualizer);
+ {
+ Mutex::Autolock l(sLock);
+ sVisualizerCallBackCookies.insert(&lpJniStorage->mCallbackData);
+ }
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
return VISUALIZER_SUCCESS;
@@ -439,13 +481,15 @@
}
// ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
- { //limit scope so that lpVisualizer is deleted before JNI storage data.
+ {
sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
if (lpVisualizer == 0) {
return;
}
lpVisualizer->release();
+ // Visualizer can still can be held by AudioEffect::EffectClient
}
// delete the JNI data
VisualizerJniStorage* lpJniStorage =
@@ -456,9 +500,22 @@
env->SetLongField(thiz, fields.fidJniData, 0);
if (lpJniStorage) {
+ {
+ Mutex::Autolock l(sLock);
+ visualizer_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+ sVisualizerCallBackCookies.erase(lpCookie);
+ while (lpCookie->busy_count > 0) {
+ if (lpCookie->cond.waitRelative(sLock,
+ milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+ NO_ERROR) {
+ break;
+ }
+ }
+ ALOG_ASSERT(lpCookie->busy_count == 0, "Unbalanced busy_count inc/dec");
env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
+ } // sLock scope
delete lpJniStorage;
}
}
@@ -714,4 +771,3 @@
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
-
diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml
index 27c903b..5916f13 100644
--- a/media/tests/EffectsTest/AndroidManifest.xml
+++ b/media/tests/EffectsTest/AndroidManifest.xml
@@ -13,6 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!--
+Make sure to enable access to the mic in settings and run:
+adb shell am compat enable ALLOW_TEST_API_ACCESS com.android.effectstest
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.effectstest">
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
index ac912c8..5f9132c 100644
--- a/media/tests/EffectsTest/res/layout/bassboosttest.xml
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -187,6 +187,11 @@
android:layout_height="wrap_content"
android:scaleType="fitXY"/>
+ <Button android:id="@+id/hammer_on_release_bug"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/hammer_on_release_bug_name">
+ </Button>
+
</LinearLayout>
</ScrollView>
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
index 18d7a36..85dabbc 100644
--- a/media/tests/EffectsTest/res/layout/visualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -175,6 +175,11 @@
</LinearLayout>
+ <Button android:id="@+id/hammer_on_release_bug"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/hammer_on_release_bug_name">
+ </Button>
+
<ImageView
android:src="@android:drawable/divider_horizontal_dark"
android:layout_width="fill_parent"
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
index 7c12da1..a44c7e9 100644
--- a/media/tests/EffectsTest/res/values/strings.xml
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -37,4 +37,6 @@
<string name="send_level_name">Send Level</string>
<!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] -->
<string name="effect_multithreaded">Multithreaded Use</string>
+ <!-- Runs a stress test for a bug related to simultaneous release of multiple effect instances [CHAR LIMIT=24] -->
+ <string name="hammer_on_release_bug_name">Hammer on release()</string>
</resources>
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
index cce2acc..a207bf1 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
@@ -17,29 +17,24 @@
package com.android.effectstest;
import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.BassBoost;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.View.OnClickListener;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.View.OnClickListener;
import android.widget.Button;
-import android.widget.TextView;
-import android.widget.EditText;
-import android.widget.SeekBar;
-import android.widget.ToggleButton;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.ToggleButton;
-import android.media.audiofx.BassBoost;
-import android.media.audiofx.AudioEffect;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
public class BassBoostTest extends Activity implements OnCheckedChangeListener {
@@ -78,6 +73,9 @@
mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
+ final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+ hammerReleaseTest.setEnabled(false);
+
getEffect(sSession);
if (mBassBoost != null) {
@@ -93,6 +91,14 @@
mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
seekBar.setOnSeekBarChangeListener(mStrength);
mStrength.setEnabled(mBassBoost.getStrengthSupported());
+
+ hammerReleaseTest.setEnabled(true);
+ hammerReleaseTest.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runHammerReleaseTest(hammerReleaseTest);
+ }
+ });
}
}
@@ -273,4 +279,52 @@
}
}
+ // Stress-tests releasing of AudioEffect by doing repeated creation
+ // and subsequent releasing. Also forces emission of callbacks from
+ // the AudioFlinger by setting a control status listener. Since all
+ // effect instances are bound to the same session, the AF will
+ // notify them about the change in their status. This can reveal racy
+ // behavior w.r.t. releasing.
+ class HammerReleaseTest extends Thread {
+ private static final int NUM_EFFECTS = 10;
+ private static final int NUM_ITERATIONS = 100;
+ private final int mSession;
+ private final Runnable mOnComplete;
+
+ HammerReleaseTest(int session, Runnable onComplete) {
+ mSession = session;
+ mOnComplete = onComplete;
+ }
+
+ @Override
+ public void run() {
+ Log.w(TAG, "HammerReleaseTest started");
+ BassBoost[] effects = new BassBoost[NUM_EFFECTS];
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ for (int j = 0; j < NUM_EFFECTS; j++) {
+ effects[j] = new BassBoost(0, mSession);
+ effects[j].setControlStatusListener(mEffectListener);
+ yield();
+ }
+ for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+ Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+ effects[j].release();
+ effects[j] = null;
+ yield();
+ }
+ }
+ Log.w(TAG, "HammerReleaseTest ended");
+ runOnUiThread(mOnComplete);
+ }
+ }
+
+ private void runHammerReleaseTest(Button controlButton) {
+ controlButton.setEnabled(false);
+ HammerReleaseTest thread = new HammerReleaseTest(sSession,
+ () -> {
+ controlButton.setEnabled(true);
+ });
+ thread.start();
+ }
+
}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
index 2e141c5..dcfe11a 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -17,6 +17,7 @@
package com.android.effectstest;
import android.app.Activity;
+import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -24,6 +25,8 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
@@ -74,11 +77,22 @@
mCallbackOn = false;
mCallbackButton.setChecked(mCallbackOn);
+ final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+ hammerReleaseTest.setEnabled(false);
+
mMultithreadedButton.setOnCheckedChangeListener(this);
if (getEffect(sSession) != null) {
mReleaseButton.setOnCheckedChangeListener(this);
mOnOffButton.setOnCheckedChangeListener(this);
mCallbackButton.setOnCheckedChangeListener(this);
+
+ hammerReleaseTest.setEnabled(true);
+ hammerReleaseTest.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runHammerReleaseTest(hammerReleaseTest);
+ }
+ });
}
}
@@ -214,4 +228,50 @@
}
}
+ // Stress-tests releasing of AudioEffect by doing repeated creation
+ // and subsequent releasing. Unlike a similar class in BassBoostTest,
+ // this one doesn't sets a control status listener because Visualizer
+ // doesn't inherit from AudioEffect and doesn't implement this method
+ // by itself.
+ class HammerReleaseTest extends Thread {
+ private static final int NUM_EFFECTS = 10;
+ private static final int NUM_ITERATIONS = 100;
+ private final int mSession;
+ private final Runnable mOnComplete;
+
+ HammerReleaseTest(int session, Runnable onComplete) {
+ mSession = session;
+ mOnComplete = onComplete;
+ }
+
+ @Override
+ public void run() {
+ Log.w(TAG, "HammerReleaseTest started");
+ Visualizer[] effects = new Visualizer[NUM_EFFECTS];
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ for (int j = 0; j < NUM_EFFECTS; j++) {
+ effects[j] = new Visualizer(mSession);
+ yield();
+ }
+ for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+ Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+ effects[j].release();
+ effects[j] = null;
+ yield();
+ }
+ }
+ Log.w(TAG, "HammerReleaseTest ended");
+ runOnUiThread(mOnComplete);
+ }
+ }
+
+ private void runHammerReleaseTest(Button controlButton) {
+ controlButton.setEnabled(false);
+ HammerReleaseTest thread = new HammerReleaseTest(sSession,
+ () -> {
+ controlButton.setEnabled(true);
+ });
+ thread.start();
+ }
+
}
diff --git a/native/webview/loader/loader.cpp b/native/webview/loader/loader.cpp
index 1265763..94e1e9f 100644
--- a/native/webview/loader/loader.cpp
+++ b/native/webview/loader/loader.cpp
@@ -121,8 +121,8 @@
jobject clazzLoader) {
int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY));
if (relro_fd == -1) {
- ALOGE("Failed to open relro file %s: %s", relro, strerror(errno));
- return LIBLOAD_FAILED_TO_OPEN_RELRO_FILE;
+ ALOGW("Failed to open relro file %s: %s", relro, strerror(errno));
+ return LIBLOAD_FAILED_TO_OPEN_RELRO_FILE;
}
android_namespace_t* ns =
android::FindNamespaceByClassLoader(env, clazzLoader);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index c24782e..3636f8f 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -38,7 +38,6 @@
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.companion.Association;
import android.companion.AssociationRequest;
import android.companion.BluetoothDeviceFilter;
import android.companion.BluetoothLeDeviceFilter;
@@ -95,7 +94,7 @@
DeviceFilterPair mSelectedDevice;
IFindDeviceCallback mFindCallback;
- AndroidFuture<Association> mServiceCallback;
+ AndroidFuture<String> mServiceCallback;
boolean mIsScanning = false;
@Nullable
CompanionDeviceActivity mActivity = null;
@@ -106,7 +105,7 @@
public void startDiscovery(AssociationRequest request,
String callingPackage,
IFindDeviceCallback findCallback,
- AndroidFuture serviceCallback) {
+ AndroidFuture<String> serviceCallback) {
Log.i(LOG_TAG,
"startDiscovery() called with: filter = [" + request
+ "], findCallback = [" + findCallback + "]"
@@ -320,9 +319,7 @@
if (callingPackage == null || deviceAddress == null) {
return;
}
- mServiceCallback.complete(new Association(
- getUserId(), deviceAddress, callingPackage, mRequest.getDeviceProfile(), false,
- System.currentTimeMillis()));
+ mServiceCallback.complete(deviceAddress);
}
void onCancel() {
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index 1ecc422..1d1316a 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -191,6 +191,13 @@
? View.VISIBLE : View.GONE);
}
+ /**
+ * Returns whether this preference is a checkbox.
+ */
+ public boolean isCheckBox() {
+ return mIsCheckBox;
+ }
+
private void init() {
if (mIsCheckBox) {
setWidgetLayoutResource(R.layout.preference_widget_checkbox);
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index f895636..dde780a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -272,9 +272,9 @@
<string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Tillad, at startindlæseren låses op"</string>
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Vil du tillade OEM-oplåsning?"</string>
<string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ADVARSEL! Funktioner, der beskytter enheden, fungerer ikke på denne enhed, når denne indstilling er aktiveret."</string>
- <string name="mock_location_app" msgid="6269380172542248304">"Vælg app til falsk placering"</string>
- <string name="mock_location_app_not_set" msgid="6972032787262831155">"Der er ikke angivet nogen app til falsk placering"</string>
- <string name="mock_location_app_set" msgid="4706722469342913843">"App til falsk placering: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="mock_location_app" msgid="6269380172542248304">"Vælg app til falsk lokation"</string>
+ <string name="mock_location_app_not_set" msgid="6972032787262831155">"Der er ikke angivet nogen app til falsk lokation"</string>
+ <string name="mock_location_app_set" msgid="4706722469342913843">"App til falsk lokation: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"Netværk"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificering af trådløs skærm"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktivér detaljeret Wi-Fi-logføring"</string>
@@ -322,8 +322,8 @@
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"Vælg logbuffere, der skal gemmes permanent på enheden"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"Vælg USB-konfiguration"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"Vælg USB-konfiguration"</string>
- <string name="allow_mock_location" msgid="2102650981552527884">"Imiterede placeringer"</string>
- <string name="allow_mock_location_summary" msgid="179780881081354579">"Tillad imiterede placeringer"</string>
+ <string name="allow_mock_location" msgid="2102650981552527884">"Imiterede lokationer"</string>
+ <string name="allow_mock_location_summary" msgid="179780881081354579">"Tillad imiterede lokationer"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"Aktivér visning af attributinspektion"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Hold altid mobildata aktiveret, selv når Wi-Fi er aktiveret (for at skifte hurtigt mellem netværk)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Brug hardwareacceleration ved netdeling, hvis det er muligt"</string>
@@ -359,7 +359,7 @@
<string name="debug_monitoring_category" msgid="1597387133765424994">"Overvågning"</string>
<string name="strict_mode" msgid="889864762140862437">"Striks tilstand aktiveret"</string>
<string name="strict_mode_summary" msgid="1838248687233554654">"Blink med skærmen, når apps foretager handlinger på hovedtråd"</string>
- <string name="pointer_location" msgid="7516929526199520173">"Markørens placering"</string>
+ <string name="pointer_location" msgid="7516929526199520173">"Markørens lokation"</string>
<string name="pointer_location_summary" msgid="957120116989798464">"Skærmoverlejringen viser de aktuelle berøringsdata"</string>
<string name="show_touches" msgid="8437666942161289025">"Vis tryk"</string>
<string name="show_touches_summary" msgid="3692861665994502193">"Vis visuel feedback ved tryk"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 229ed45..80439d9 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -74,7 +74,7 @@
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Шектеулі байланыс"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string>
- <string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Есептік жазбаға кіру керек"</string>
+ <string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Аккаунтқа кіру керек"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Кіру нүктесі уақытша бос емес"</string>
<string name="connected_via_carrier" msgid="1968057009076191514">"%1$s арқылы қосылды"</string>
<string name="available_via_carrier" msgid="465598683092718294">"%1$s арқылы қолжетімді"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 485f40a..4365b20 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -579,7 +579,7 @@
<string name="user_setup_dialog_message" msgid="269931619868102841">"तो वापरकर्ता डिव्हाइसजवळ आहे आणि त्याचे स्थान सेट करण्यासाठी उपलब्ध आहे याची खात्री करा"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"आता प्रोफाईल सेट करायचा?"</string>
<string name="user_setup_button_setup_now" msgid="1708269547187760639">"आता सेट करा"</string>
- <string name="user_setup_button_setup_later" msgid="8712980133555493516">"आत्ता नाही"</string>
+ <string name="user_setup_button_setup_later" msgid="8712980133555493516">"आता नको"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"जोडा"</string>
<string name="user_new_user_name" msgid="60979820612818840">"नवीन वापरकर्ता"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"नवीन प्रोफाईल"</string>
diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml
index c062007..35cc015 100644
--- a/packages/SettingsLib/res/values-sk/arrays.xml
+++ b/packages/SettingsLib/res/values-sk/arrays.xml
@@ -59,7 +59,7 @@
<item msgid="6421717003037072581">"Vždy používať kontrolu HDCP"</item>
</string-array>
<string-array name="bt_hci_snoop_log_entries">
- <item msgid="695678520785580527">"Deaktivované"</item>
+ <item msgid="695678520785580527">"Vypnuté"</item>
<item msgid="6336372935919715515">"Aktivované filtrované"</item>
<item msgid="2779123106632690576">"Aktivované"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 421a428..b189595 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -322,8 +322,8 @@
<string name="select_logpersist_dialog_title" msgid="7745193591195485594">"పరికరంలో నిరంతరం నిల్వ చేయాల్సిన లాగ్ బఫర్లను ఎంచుకోండి"</string>
<string name="select_usb_configuration_title" msgid="6339801314922294586">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
<string name="select_usb_configuration_dialog_title" msgid="3579567144722589237">"USB కాన్ఫిగరేషన్ని ఎంచుకోండి"</string>
- <string name="allow_mock_location" msgid="2102650981552527884">"అనుకృత స్థానాలను అనుమతించు"</string>
- <string name="allow_mock_location_summary" msgid="179780881081354579">"అనుకృత స్థానాలను అనుమతించు"</string>
+ <string name="allow_mock_location" msgid="2102650981552527884">"డమ్మీ లొకేషన్లను అనుమతించండి"</string>
+ <string name="allow_mock_location_summary" msgid="179780881081354579">"డమ్మీ లొకేషన్లను అనుమతించండి"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"వీక్షణ అట్రిబ్యూట్ పర్యవేక్షణను ఎనేబుల్ చేయి"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"ఎల్లప్పుడూ మొబైల్ డేటాను యాక్టివ్గా ఉంచు, Wi‑Fi యాక్టివ్గా ఉన్నా కూడా (వేగవంతమైన నెట్వర్క్ మార్పు కోసం)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"అందుబాటులో ఉంటే టెథెరింగ్ హార్డ్వేర్ వేగవృద్ధిని ఉపయోగించండి"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
index bc0c6f3..794b0eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib;
+import com.android.settingslib.mobile.TelephonyIcons;
+
import java.text.SimpleDateFormat;
import java.util.Objects;
@@ -40,9 +42,17 @@
// For logging.
public final String name;
- public IconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
- int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
- int discContentDesc) {
+ public IconGroup(
+ String name,
+ int[][] sbIcons,
+ int[][] qsIcons,
+ int[] contentDesc,
+ int sbNullState,
+ int qsNullState,
+ int sbDiscState,
+ int qsDiscState,
+ int discContentDesc
+ ) {
this.name = name;
this.sbIcons = sbIcons;
this.qsIcons = qsIcons;
@@ -131,6 +141,19 @@
&& other.activityOut == activityOut
&& other.rssi == rssi;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ connected,
+ enabled,
+ level,
+ inetCondition,
+ iconGroup,
+ activityIn,
+ activityOut,
+ rssi);
+ }
}
/**
@@ -139,18 +162,31 @@
public static class MobileIconGroup extends IconGroup {
public final int dataContentDescription; // mContentDescriptionDataType
public final int dataType;
- public final boolean isWide;
- public final int qsDataType;
- public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc,
- int sbNullState, int qsNullState, int sbDiscState, int qsDiscState,
- int discContentDesc, int dataContentDesc, int dataType, boolean isWide) {
- super(name, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState,
- qsDiscState, discContentDesc);
+ public MobileIconGroup(
+ String name,
+ int[][] sbIcons,
+ int[][] qsIcons,
+ int[] contentDesc,
+ int sbNullState,
+ int qsNullState,
+ int sbDiscState,
+ int qsDiscState,
+ int discContentDesc,
+ int dataContentDesc,
+ int dataType
+ ) {
+ super(name,
+ sbIcons,
+ qsIcons,
+ contentDesc,
+ sbNullState,
+ qsNullState,
+ sbDiscState,
+ qsDiscState,
+ discContentDesc);
this.dataContentDescription = dataContentDesc;
this.dataType = dataType;
- this.isWide = isWide;
- this.qsDataType = dataType; // TODO: remove this field
}
}
@@ -187,6 +223,27 @@
defaultDataOff = state.defaultDataOff;
}
+ /** @return true if this state is disabled or not default data */
+ public boolean isDataDisabledOrNotDefault() {
+ return (iconGroup == TelephonyIcons.DATA_DISABLED
+ || (iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)) && userSetup;
+ }
+
+ /** @return if this state is considered to have inbound activity */
+ public boolean hasActivityIn() {
+ return dataConnected && !carrierNetworkChangeMode && activityIn;
+ }
+
+ /** @return if this state is considered to have outbound activity */
+ public boolean hasActivityOut() {
+ return dataConnected && !carrierNetworkChangeMode && activityOut;
+ }
+
+ /** @return true if this state should show a RAT icon in quick settings */
+ public boolean showQuickSettingsRatIcon() {
+ return dataConnected || isDataDisabledOrNotDefault();
+ }
+
@Override
protected void toString(StringBuilder builder) {
super.toString(builder);
@@ -202,23 +259,40 @@
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
.append(',');
builder.append("userSetup=").append(userSetup).append(',');
- builder.append("defaultDataOff=").append(defaultDataOff);
+ builder.append("defaultDataOff=").append(defaultDataOff).append(',');
+ builder.append("showQuickSettingsRatIcon=").append(showQuickSettingsRatIcon());
}
@Override
public boolean equals(Object o) {
return super.equals(o)
- && Objects.equals(((MobileState) o).networkName, networkName)
- && Objects.equals(((MobileState) o).networkNameData, networkNameData)
- && ((MobileState) o).dataSim == dataSim
- && ((MobileState) o).dataConnected == dataConnected
- && ((MobileState) o).isEmergency == isEmergency
- && ((MobileState) o).airplaneMode == airplaneMode
- && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
- && ((MobileState) o).userSetup == userSetup
- && ((MobileState) o).isDefault == isDefault
- && ((MobileState) o).roaming == roaming
- && ((MobileState) o).defaultDataOff == defaultDataOff;
+ && Objects.equals(((MobileState) o).networkName, networkName)
+ && Objects.equals(((MobileState) o).networkNameData, networkNameData)
+ && ((MobileState) o).dataSim == dataSim
+ && ((MobileState) o).dataConnected == dataConnected
+ && ((MobileState) o).isEmergency == isEmergency
+ && ((MobileState) o).airplaneMode == airplaneMode
+ && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
+ && ((MobileState) o).userSetup == userSetup
+ && ((MobileState) o).isDefault == isDefault
+ && ((MobileState) o).roaming == roaming
+ && ((MobileState) o).defaultDataOff == defaultDataOff;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(),
+ networkName,
+ networkNameData,
+ dataSim,
+ dataConnected,
+ isEmergency,
+ airplaneMode,
+ carrierNetworkChangeMode,
+ userSetup,
+ isDefault,
+ roaming,
+ defaultDataOff);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
new file mode 100644
index 0000000..754914f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CSIP Set Coordinator handles Bluetooth CSIP Set Coordinator role profile.
+ */
+public class CsipSetCoordinatorProfile implements LocalBluetoothProfile {
+ private static final String TAG = "CsipSetCoordinatorProfile";
+ private static final boolean VDBG = true;
+
+ private Context mContext;
+
+ private BluetoothCsipSetCoordinator mService;
+ private boolean mIsProfileReady;
+
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final String NAME = "CSIP Set Coordinator";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 1;
+
+ // These callbacks run on the main thread.
+ private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener {
+ @RequiresApi(32)
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (VDBG) {
+ Log.d(TAG, "Bluetooth service connected");
+ }
+ mService = (BluetoothCsipSetCoordinator) proxy;
+ // We just bound to the service, so refresh the UI for any connected CSIP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ if (VDBG) {
+ Log.d(TAG, "CsipSetCoordinatorProfile found new device: " + nextDevice);
+ }
+ device = mDeviceManager.addDevice(nextDevice);
+ }
+ device.onProfileStateChanged(
+ CsipSetCoordinatorProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady = true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (VDBG) {
+ Log.d(TAG, "Bluetooth service disconnected");
+ }
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady = false;
+ }
+ }
+
+ CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mContext = context;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+
+ BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+ new CoordinatedSetServiceListener(), BluetoothProfile.CSIP_SET_COORDINATOR);
+ }
+
+ /**
+ * Get CSIP devices matching connection states{
+ *
+ * @code BluetoothProfile.STATE_CONNECTED,
+ * @code BluetoothProfile.STATE_CONNECTING,
+ * @code BluetoothProfile.STATE_DISCONNECTING}
+ *
+ * @return Matching device list
+ */
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ /**
+ * Gets the connection status of the device.
+ *
+ * @code BluetoothProfile.STATE_CONNECTED,
+ * @code BluetoothProfile.STATE_CONNECTING,
+ * @code BluetoothProfile.STATE_DISCONNECTING}
+ *
+ * @return Connection status, {@code BluetoothProfile.STATE_DISCONNECTED} if unknown.
+ */
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ @Override
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ @Override
+ public int getProfileId() {
+ return BluetoothProfile.CSIP_SET_COORDINATOR;
+ }
+
+ @Override
+ public boolean accessProfileEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
+ if (mService == null || device == null) {
+ return false;
+ }
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
+ if (mService == null || device == null) {
+ return CONNECTION_POLICY_FORBIDDEN;
+ }
+ return mService.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
+ if (mService == null || device == null) {
+ return false;
+ }
+ if (enabled) {
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ }
+ } else {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ }
+
+ return isEnabled;
+ }
+
+ @Override
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ @Override
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.summary_empty;
+ }
+
+ @Override
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ return BluetoothUtils.getConnectionStateSummary(state);
+ }
+
+ @Override
+ public int getDrawableResource(BluetoothClass btClass) {
+ return 0;
+ }
+
+ /**
+ * Return the profile name as a string.
+ */
+ public String toString() {
+ return NAME;
+ }
+
+ @RequiresApi(32)
+ protected void finalize() {
+ if (VDBG) {
+ Log.d(TAG, "finalize()");
+ }
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
+ BluetoothProfile.CSIP_SET_COORDINATOR, mService);
+ mService = null;
+ } catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up CSIP Set Coordinator proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index c47ce41..201825f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
@@ -100,6 +101,7 @@
private PbapClientProfile mPbapClientProfile;
private PbapServerProfile mPbapProfile;
private HearingAidProfile mHearingAidProfile;
+ private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
private SapProfile mSapProfile;
private VolumeControlProfile mVolumeControlProfile;
@@ -230,7 +232,16 @@
// Note: no event handler for VCP, only for being connectable.
mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile);
}
-
+ if (mCsipSetCoordinatorProfile == null
+ && supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) {
+ if (DEBUG) {
+ Log.d(TAG, "Adding local CSIP set coordinator profile");
+ }
+ mCsipSetCoordinatorProfile =
+ new CsipSetCoordinatorProfile(mContext, mDeviceManager, this);
+ addProfile(mCsipSetCoordinatorProfile, mCsipSetCoordinatorProfile.NAME,
+ BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED);
+ }
mEventManager.registerProfileIntentReceiver();
}
@@ -466,6 +477,10 @@
return mHidDeviceProfile;
}
+ public CsipSetCoordinatorProfile getCsipSetCoordinatorProfile() {
+ return mCsipSetCoordinatorProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -586,6 +601,12 @@
removedProfiles.remove(mVolumeControlProfile);
}
+ if (mCsipSetCoordinatorProfile != null
+ && ArrayUtils.contains(uuids, BluetoothUuid.COORDINATED_SET)) {
+ profiles.add(mCsipSetCoordinatorProfile);
+ removedProfiles.remove(mCsipSetCoordinatorProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index c501b3a..2e8f368 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -110,6 +110,18 @@
}
/**
+ * Check if target package is in allow list except idle app
+ */
+ public boolean isAllowlistedExceptIdle(String pkg) {
+ try {
+ return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ return true;
+ }
+ }
+
+ /**
*
* @param pkgs a list of packageName
* @return true when one of package is in allow list
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index f8565bc..d4e58f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -50,178 +50,194 @@
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.carrier_network_change_mode,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup THREE_G = new MobileIconGroup(
"3G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3g,
- TelephonyIcons.ICON_3G,
- true);
+ TelephonyIcons.ICON_3G
+ );
public static final MobileIconGroup WFC = new MobileIconGroup(
"WFC",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0, 0, false);
+ 0,
+ 0);
public static final MobileIconGroup UNKNOWN = new MobileIconGroup(
"Unknown",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- 0, 0, false);
+ 0,
+ 0);
public static final MobileIconGroup E = new MobileIconGroup(
"E",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_edge,
- TelephonyIcons.ICON_E,
- false);
+ TelephonyIcons.ICON_E
+ );
public static final MobileIconGroup ONE_X = new MobileIconGroup(
"1X",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_cdma,
- TelephonyIcons.ICON_1X,
- true);
+ TelephonyIcons.ICON_1X
+ );
public static final MobileIconGroup G = new MobileIconGroup(
"G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_gprs,
- TelephonyIcons.ICON_G,
- false);
+ TelephonyIcons.ICON_G
+ );
public static final MobileIconGroup H = new MobileIconGroup(
"H",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g,
- TelephonyIcons.ICON_H,
- false);
+ TelephonyIcons.ICON_H
+ );
public static final MobileIconGroup H_PLUS = new MobileIconGroup(
"H+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_3_5g_plus,
- TelephonyIcons.ICON_H_PLUS,
- false);
+ TelephonyIcons.ICON_H_PLUS
+ );
public static final MobileIconGroup FOUR_G = new MobileIconGroup(
"4G",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g,
- TelephonyIcons.ICON_4G,
- true);
+ TelephonyIcons.ICON_4G
+ );
public static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
"4G+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_4g_plus,
- TelephonyIcons.ICON_4G_PLUS,
- true);
+ TelephonyIcons.ICON_4G_PLUS
+ );
public static final MobileIconGroup LTE = new MobileIconGroup(
"LTE",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte,
- TelephonyIcons.ICON_LTE,
- true);
+ TelephonyIcons.ICON_LTE
+ );
public static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
"LTE+",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_lte_plus,
- TelephonyIcons.ICON_LTE_PLUS,
- true);
+ TelephonyIcons.ICON_LTE_PLUS
+ );
public static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup(
"5Ge",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5ge_html,
- TelephonyIcons.ICON_5G_E,
- true);
+ TelephonyIcons.ICON_5G_E
+ );
public static final MobileIconGroup NR_5G = new MobileIconGroup(
"5G",
@@ -234,8 +250,8 @@
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g,
- TelephonyIcons.ICON_5G,
- true);
+ TelephonyIcons.ICON_5G
+ );
public static final MobileIconGroup NR_5G_PLUS = new MobileIconGroup(
"5G_PLUS",
@@ -248,34 +264,36 @@
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_5g_plus,
- TelephonyIcons.ICON_5G_PLUS,
- true);
+ TelephonyIcons.ICON_5G_PLUS
+ );
public static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
"DataDisabled",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.cell_data_off_content_description,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup NOT_DEFAULT_DATA = new MobileIconGroup(
"NotDefaultData",
null,
null,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
+ 0,
+ 0,
0,
0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.not_default_data_content_description,
- 0,
- false);
+ 0
+ );
public static final MobileIconGroup CARRIER_MERGED_WIFI = new MobileIconGroup(
"CWF",
@@ -288,8 +306,8 @@
/* qsDiscState= */ 0,
AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
R.string.data_connection_carrier_wifi,
- TelephonyIcons.ICON_CWF,
- /* isWide= */ true);
+ TelephonyIcons.ICON_CWF
+ );
// When adding a new MobileIconGround, check if the dataContentDescription has to be filtered
// in QSCarrier#hasValidTypeContentDescription
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
new file mode 100644
index 0000000..92a32bc
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.android.settingslib.mobile.TelephonyIcons;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class MobileStateTest {
+
+ private SignalIcon.MobileState mState = new SignalIcon.MobileState();
+
+ @Before
+ public void setUp() {
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_dataDisabled() {
+ mState.iconGroup = TelephonyIcons.DATA_DISABLED;
+ mState.userSetup = true;
+
+ assertTrue(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_notDefaultData() {
+ mState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
+ mState.userSetup = true;
+
+ assertTrue(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testIsDataDisabledOrNotDefault_notDisabled() {
+ mState.iconGroup = TelephonyIcons.G;
+ mState.userSetup = true;
+
+ assertFalse(mState.isDataDisabledOrNotDefault());
+ }
+
+ @Test
+ public void testHasActivityIn_noData_noActivity() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = false;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_noData_activityIn() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = true;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_dataConnected_activityIn() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityIn = true;
+
+ assertTrue(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityIn_carrierNetworkChange() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = true;
+ mState.activityIn = true;
+
+ assertFalse(mState.hasActivityIn());
+ }
+
+ @Test
+ public void testHasActivityOut_noData_noActivity() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = false;
+
+ assertFalse(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_noData_activityOut() {
+ mState.dataConnected = false;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = true;
+
+ assertFalse(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_dataConnected_activityOut() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = false;
+ mState.activityOut = true;
+
+ assertTrue(mState.hasActivityOut());
+ }
+
+ @Test
+ public void testHasActivityOut_carrierNetworkChange() {
+ mState.dataConnected = true;
+ mState.carrierNetworkChangeMode = true;
+ mState.activityOut = true;
+
+ assertFalse(mState.hasActivityOut());
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 4f11fb1..6caf762 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -151,4 +151,14 @@
assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
}
+
+ @Test
+ public void testIsPowerSaveWhitelistExceptIdleApp() throws Exception {
+ doReturn(true).when(mDeviceIdleService)
+ .isPowerSaveWhitelistExceptIdleApp(PACKAGE_ONE);
+
+ mPowerAllowlistBackend.refreshList();
+
+ assertThat(mPowerAllowlistBackend.isAllowlistedExceptIdle(PACKAGE_ONE)).isTrue();
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 25211b4..fd2c803 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -918,9 +918,10 @@
}
}
+ @SuppressWarnings("GuardedBy")
private void dumpSettingsLocked(SettingsState settingsState, PrintWriter pw) {
List<String> names = settingsState.getSettingNamesLocked();
-
+ pw.println("version: " + settingsState.getVersionLocked());
final int nameCount = names.size();
for (int i = 0; i < nameCount; i++) {
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index c23efc3..049d2ac 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Vælg for at dele din fejlrapport uden et screenshot, eller vent på, at et screenshot er klar"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tryk for at dele din fejlrapport uden et screenshot, eller vent på, at screenshott fuldføres"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tryk for at dele din fejlrapport uden et screenshot, eller vent på, at screenshott fuldføres"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"Fejlrapporter indeholder data fra systemets forskellige logfiler, og der kan være følsomme data imellem (f.eks. appforbrug og placeringsdata). Del kun fejlrapporter med personer og apps, du har tillid til."</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"Fejlrapporter indeholder data fra systemets forskellige logfiler, og der kan være følsomme data imellem (f.eks. appforbrug og lokationsdata). Del kun fejlrapporter med personer og apps, du har tillid til."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Vis ikke igen"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Fejlrapporter"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Fejlrapportfilen kunne ikke læses"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 228ee40..fabc524 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -93,6 +93,7 @@
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
+ "monet",
"dagger2",
"jsr330",
"lottie",
@@ -181,6 +182,7 @@
"mockito-target-extended-minus-junit4",
"testables",
"truth-prebuilt",
+ "monet",
"dagger2",
"jsr330",
"WindowManager-Shell",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 31cf530..45e0345 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -22,6 +22,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
},
@@ -94,6 +97,9 @@
},
{
"exclude-annotation": "android.platform.helpers.Staging"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
}
@@ -110,6 +116,9 @@
},
{
"exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
}
@@ -126,5 +135,24 @@
}
]
}
+ ],
+ "large-screen-postsubmit": [
+ {
+ "name": "PlatformScenarioTests",
+ "options" : [
+ {
+ "include-filter": "android.platform.test.scenario.sysui"
+ },
+ {
+ "include-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/monet/Android.bp b/packages/SystemUI/monet/Android.bp
new file mode 100644
index 0000000..507ea25
--- /dev/null
+++ b/packages/SystemUI/monet/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "monet",
+ platform_apis: true,
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.core_core",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+}
diff --git a/packages/SystemUI/monet/AndroidManifest.xml b/packages/SystemUI/monet/AndroidManifest.xml
new file mode 100644
index 0000000..1fab528
--- /dev/null
+++ b/packages/SystemUI/monet/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.monet">
+</manifest>
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
new file mode 100644
index 0000000..b8039e1
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet
+
+import android.annotation.ColorInt
+import android.app.WallpaperColors
+import android.graphics.Color
+import com.android.internal.graphics.ColorUtils
+import com.android.internal.graphics.cam.Cam
+import com.android.internal.graphics.cam.CamUtils.lstarFromInt
+import kotlin.math.absoluteValue
+import kotlin.math.roundToInt
+
+const val TAG = "ColorScheme"
+
+const val ACCENT1_CHROMA = 48.0f
+const val ACCENT2_CHROMA = 16.0f
+const val ACCENT3_CHROMA = 32.0f
+const val ACCENT3_HUE_SHIFT = 60.0f
+
+const val NEUTRAL1_CHROMA = 4.0f
+const val NEUTRAL2_CHROMA = 8.0f
+
+const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
+
+const val MIN_CHROMA = 15
+const val MIN_LSTAR = 10
+
+public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
+
+ val accent1: List<Int>
+ val accent2: List<Int>
+ val accent3: List<Int>
+ val neutral1: List<Int>
+ val neutral2: List<Int>
+
+ constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
+ this(getSeedColor(wallpaperColors), darkTheme)
+
+ val allAccentColors: List<Int>
+ get() {
+ val allColors = mutableListOf<Int>()
+ allColors.addAll(accent1)
+ allColors.addAll(accent2)
+ allColors.addAll(accent3)
+ return allColors
+ }
+
+ val allNeutralColors: List<Int>
+ get() {
+ val allColors = mutableListOf<Int>()
+ allColors.addAll(neutral1)
+ allColors.addAll(neutral2)
+ return allColors
+ }
+
+ val backgroundColor
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1[8] else neutral1[0], 0xFF)
+
+ val accentColor
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
+
+ init {
+ val seedArgb = if (seed == Color.TRANSPARENT) GOOGLE_BLUE else seed
+ val camSeed = Cam.fromInt(seedArgb)
+ val hue = camSeed.hue
+ val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
+ accent1 = Shades.of(hue, chroma).toList()
+ accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
+ accent3 = Shades.of(hue + ACCENT3_HUE_SHIFT, ACCENT3_CHROMA).toList()
+ neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
+ neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
+ }
+
+ override fun toString(): String {
+ return "ColorScheme {\n" +
+ " neutral1: ${humanReadable(neutral1)}\n" +
+ " neutral2: ${humanReadable(neutral2)}\n" +
+ " accent1: ${humanReadable(accent1)}\n" +
+ " accent2: ${humanReadable(accent2)}\n" +
+ " accent3: ${humanReadable(accent3)}\n" +
+ "}"
+ }
+
+ companion object {
+ /**
+ * Identifies a color to create a color scheme from.
+ *
+ * @param wallpaperColors Colors extracted from an image via quantization.
+ * @return ARGB int representing the color
+ */
+ @JvmStatic
+ @ColorInt
+ fun getSeedColor(wallpaperColors: WallpaperColors): Int {
+ return getSeedColors(wallpaperColors).first()
+ }
+
+ /**
+ * Filters and ranks colors from WallpaperColors.
+ *
+ * @param wallpaperColors Colors extracted from an image via quantization.
+ * @return List of ARGB ints, ordered from highest scoring to lowest.
+ */
+ @JvmStatic
+ fun getSeedColors(wallpaperColors: WallpaperColors): List<Int> {
+ val totalPopulation = wallpaperColors.allColors.values.reduce { a, b -> a + b }
+ .toDouble()
+ val totalPopulationMeaningless = (totalPopulation == 0.0)
+ if (totalPopulationMeaningless) {
+ // WallpaperColors with a population of 0 indicate the colors didn't come from
+ // quantization. Instead of scoring, trust the ordering of the provided primary
+ // secondary/tertiary colors.
+ //
+ // In this case, the colors are usually from a Live Wallpaper.
+ val distinctColors = wallpaperColors.mainColors.map {
+ it.toArgb()
+ }.distinct().filter {
+ val cam = Cam.fromInt(it)
+ val lstar = lstarFromInt(it)
+ cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR
+ }.toList()
+
+ if (distinctColors.isEmpty()) {
+ return listOf(GOOGLE_BLUE)
+ }
+ return distinctColors
+ }
+
+ val intToProportion = wallpaperColors.allColors.mapValues {
+ it.value.toDouble() / totalPopulation
+ }
+ val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
+
+ // Get an array with 360 slots. A slot contains the percentage of colors with that hue.
+ val hueProportions = huePopulations(intToCam, intToProportion)
+ // Map each color to the percentage of the image with its hue.
+ val intToHueProportion = wallpaperColors.allColors.mapValues {
+ val cam = intToCam[it.key]!!
+ val hue = cam.hue.roundToInt()
+ var proportion = 0.0
+ for (i in hue - 15..hue + 15) {
+ proportion += hueProportions[wrapDegrees(i)]
+ }
+ proportion
+ }
+ // Remove any inappropriate seed colors. For example, low chroma colors look grayscale
+ // raising their chroma will turn them to a much louder color that may not have been
+ // in the image.
+ val filteredIntToCam = intToCam.filter {
+ val cam = it.value
+ val lstar = lstarFromInt(it.key)
+ val proportion = intToHueProportion[it.key]!!
+ cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR &&
+ (totalPopulationMeaningless || proportion > 0.01)
+ }
+ // Sort the colors by score, from high to low.
+ val seeds = mutableListOf<Int>()
+ val intToScoreIntermediate = filteredIntToCam.mapValues {
+ score(it.value, intToHueProportion[it.key]!!)
+ }
+ val intToScore = intToScoreIntermediate.entries.toMutableList()
+ intToScore.sortByDescending { it.value }
+
+ // Go through the colors, from high score to low score. If there isn't already a seed
+ // color with a hue close to color being examined, add the color being examined to the
+ // seed colors.
+ for (entry in intToScore) {
+ val int = entry.key
+ val existingSeedNearby = seeds.find {
+ val hueA = intToCam[int]!!.hue
+ val hueB = intToCam[it]!!.hue
+ hueDiff(hueA, hueB) < 15 } != null
+ if (existingSeedNearby) {
+ continue
+ }
+ seeds.add(int)
+ }
+
+ if (seeds.isEmpty()) {
+ // Use gBlue 500 if there are 0 colors
+ seeds.add(GOOGLE_BLUE)
+ }
+
+ return seeds
+ }
+
+ private fun wrapDegrees(degrees: Int): Int {
+ return when {
+ degrees < 0 -> {
+ (degrees % 360) + 360
+ }
+ degrees >= 360 -> {
+ degrees % 360
+ }
+ else -> {
+ degrees
+ }
+ }
+ }
+
+ private fun hueDiff(a: Float, b: Float): Float {
+ return 180f - ((a - b).absoluteValue - 180f).absoluteValue
+ }
+
+ private fun humanReadable(colors: List<Int>): String {
+ return colors.joinToString { "#" + Integer.toHexString(it) }
+ }
+
+ private fun score(cam: Cam, proportion: Double): Double {
+ val proportionScore = 0.7 * 100.0 * proportion
+ val chromaScore = if (cam.chroma < ACCENT1_CHROMA) 0.1 * (cam.chroma - ACCENT1_CHROMA)
+ else 0.3 * (cam.chroma - ACCENT1_CHROMA)
+ return chromaScore + proportionScore
+ }
+
+ private fun huePopulations(
+ camByColor: Map<Int, Cam>,
+ populationByColor: Map<Int, Double>
+ ): List<Double> {
+ val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
+
+ for (entry in populationByColor.entries) {
+ val population = populationByColor[entry.key]!!
+ val cam = camByColor[entry.key]!!
+ val hue = cam.hue.roundToInt() % 360
+ huePopulation[hue] = huePopulation[hue] + population
+ }
+
+ return huePopulation
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
new file mode 100644
index 0000000..498b7dd
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.monet;
+
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
+
+
+/**
+ * Generate sets of colors that are shades of the same color
+ */
+@VisibleForTesting
+public class Shades {
+ /**
+ * Combining the ability to convert between relative luminance and perceptual luminance with
+ * contrast leads to a design system that can be based on a linear value to determine contrast,
+ * rather than a ratio.
+ *
+ * This codebase implements a design system that has that property, and as a result, we can
+ * guarantee that any shades 5 steps from each other have a contrast ratio of at least 4.5.
+ * 4.5 is the requirement for smaller text contrast in WCAG 2.1 and earlier.
+ *
+ * However, lstar 50 does _not_ have a contrast ratio >= 4.5 with lstar 100.
+ * lstar 49.6 is the smallest lstar that will lead to a contrast ratio >= 4.5 with lstar 100,
+ * and it also contrasts >= 4.5 with lstar 100.
+ */
+ public static final float MIDDLE_LSTAR = 49.6f;
+
+ /**
+ * Generate shades of a color. Ordered in lightness _descending_.
+ * <p>
+ * The first shade will be at 95% lightness, the next at 90, 80, etc. through 0.
+ *
+ * @param hue hue in CAM16 color space
+ * @param chroma chroma in CAM16 color space
+ * @return shades of a color, as argb integers. Ordered by lightness descending.
+ */
+ public static @ColorInt int[] of(float hue, float chroma) {
+ int[] shades = new int[12];
+ shades[0] = ColorUtils.CAMToColor(hue, chroma, 99);
+ shades[1] = ColorUtils.CAMToColor(hue, chroma, 95);
+ for (int i = 2; i < 12; i++) {
+ float lStar = (i == 6) ? MIDDLE_LSTAR : 100 - 10 * (i - 1);
+ shades[i] = ColorUtils.CAMToColor(hue, chroma, lStar);
+ }
+ return shades;
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 883f4de..94fdbae 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -183,12 +183,6 @@
public boolean canBeDismissed();
/**
- * Informs the menu whether dismiss gestures are left-to-right or right-to-left.
- */
- default void setDismissRtl(boolean dismissRtl) {
- }
-
- /**
* Determines whether the menu should remain open given its current state, or snap closed.
* @return true if the menu should remain open, false otherwise.
*/
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 871b1c4..624ee9f 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -146,4 +146,8 @@
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
+
+ <style name="TextAppearance.Keyguard.BottomArea.Button">
+ <item name="android:shadowRadius">0</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res-product/values-kk/strings.xml b/packages/SystemUI/res-product/values-kk/strings.xml
index d2aec4b..2629667 100644
--- a/packages/SystemUI/res-product/values-kk/strings.xml
+++ b/packages/SystemUI/res-product/values-kk/strings.xml
@@ -38,8 +38,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Телефон құлпын ашуға <xliff:g id="NUMBER_0">%1$d</xliff:g> рет сәтсіз әрекет жасалды. <xliff:g id="NUMBER_1">%2$d</xliff:g> әрекет қалды. Одан кейін жұмыс профилі өшіріліп, оның бүкіл деректері жойылады."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Планшет құлпын ашуға <xliff:g id="NUMBER">%d</xliff:g> рет сәтсіз әрекет жасалды. Жұмыс профилі өшіріліп, оның бүкіл деректері жойылады."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Телефон құлпын ашуға <xliff:g id="NUMBER">%d</xliff:g> рет сәтсіз әрекет жасалды. Жұмыс профилі өшіріліп, оның бүкіл деректері жойылады."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Құлыпты ашу өрнегі <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. <xliff:g id="NUMBER_1">%2$d</xliff:g> әрекет қалды. Одан кейін планшетті есептік жазба арқылы ашу сұралады. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Құлыпты ашу өрнегі <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. <xliff:g id="NUMBER_1">%2$d</xliff:g> әрекет қалды. Одан кейін телефонды есептік жазба арқылы ашу сұралады. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Құлыпты ашу өрнегі <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. <xliff:g id="NUMBER_1">%2$d</xliff:g> әрекет қалды. Одан кейін планшетті аккаунт арқылы ашу сұралады. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Құлыпты ашу өрнегі <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. <xliff:g id="NUMBER_1">%2$d</xliff:g> әрекет қалды. Одан кейін телефонды аккаунт арқылы ашу сұралады. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Басқа опцияларды көру үшін телефон құлпын ашыңыз."</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Басқа опцияларды көру үшін планшет құлпын ашыңыз."</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Басқа опцияларды көру үшін құрылғы құлпын ашыңыз."</string>
diff --git a/packages/SystemUI/res/drawable/logout_button_background.xml b/packages/SystemUI/res/drawable/logout_button_background.xml
index eafd663..34434be 100644
--- a/packages/SystemUI/res/drawable/logout_button_background.xml
+++ b/packages/SystemUI/res/drawable/logout_button_background.xml
@@ -17,7 +17,8 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="@color/logout_button_bg_color"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
<corners android:radius="@dimen/logout_button_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index b841419..d286832 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -39,7 +39,7 @@
android:layout_width="wrap_content"
android:layout_height="32dp"
android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textSize="24sp"/>
<TextView
@@ -50,7 +50,7 @@
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
- android:fontFamily="google-sans"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
android:textSize="14sp"/>
</LinearLayout>
@@ -87,24 +87,49 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
- android:id="@+id/internet_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
+ android:id="@+id/ethernet_layout"
+ style="@style/InternetDialog.Network"
+ android:background="@drawable/settingslib_switch_bar_bg_on"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="false">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:autoMirrored="true"
+ android:src="@drawable/stat_sys_ethernet_fully"
+ android:tint="@color/connected_network_primary_color"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:orientation="vertical"
+ android:clickable="false">
+ <TextView
+ android:text="@string/ethernet_label"
+ style="@style/InternetDialog.NetworkTitle.Active"/>
+ <TextView
+ android:text="@string/to_switch_networks_disconnect_ethernet"
+ style="@style/InternetDialog.NetworkSummary.Active"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
android:id="@+id/mobile_network_layout"
- android:layout_width="match_parent"
- android:layout_height="88dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:layout_gravity="center_vertical|start"
- android:orientation="horizontal"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ style="@style/InternetDialog.Network">
<FrameLayout
android:layout_width="24dp"
@@ -121,7 +146,6 @@
<LinearLayout
android:layout_weight="1"
- android:id="@+id/mobile_network_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
@@ -129,28 +153,10 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/mobile_title"
- android:textDirection="locale"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="7dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkTitle"/>
<TextView
android:id="@+id/mobile_summary"
- android:textDirection="locale"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="34dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
<FrameLayout
@@ -172,17 +178,9 @@
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center"
- android:orientation="horizontal"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ android:gravity="center">
<FrameLayout
android:layout_weight="1"
@@ -193,13 +191,10 @@
<TextView
android:id="@+id/wifi_toggle_title"
android:text="@string/turn_on_wifi"
- android:textDirection="locale"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:fontFamily="google-sans"/>
+ android:textAppearance="@style/TextAppearance.InternetDialog"/>
</FrameLayout>
<FrameLayout
@@ -222,18 +217,12 @@
<LinearLayout
android:id="@+id/wifi_connected_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:layout_gravity="center_vertical|start"
- android:clickable="true"
- android:focusable="true"
- android:visibility="gone"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="horizontal"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
android:paddingStart="20dp"
- android:paddingEnd="24dp">
+ android:paddingEnd="24dp"
+ android:background="@drawable/settingslib_switch_bar_bg_on"
+ android:visibility="gone">
<FrameLayout
android:layout_width="24dp"
@@ -248,7 +237,6 @@
</FrameLayout>
<LinearLayout
- android:id="@+id/wifi_connected_list"
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
@@ -258,26 +246,11 @@
android:gravity="start|center_vertical">
<TextView
android:id="@+id/wifi_connected_title"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkTitle.Active"
+ android:textSize="14sp"/>
<TextView
android:id="@+id/wifi_connected_summary"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorTertiary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ style="@style/InternetDialog.NetworkSummary.Active"/>
</LinearLayout>
<FrameLayout
@@ -340,13 +313,11 @@
android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
<TextView
android:text="@string/see_all_networks"
- android:textDirection="locale"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ android:textAppearance="@style/TextAppearance.InternetDialog"
+ android:textSize="14sp"/>
</FrameLayout>
</LinearLayout>
@@ -366,7 +337,7 @@
android:textColor="?android:attr/textColorPrimary"
android:text="@string/inline_done_button"
android:textSize="14sp"
- android:fontFamily="google-sans"/>
+ android:fontFamily="@*android:string/config_headlineFontFamily"/>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 05352c5..868331e 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -24,21 +24,15 @@
<LinearLayout
android:id="@+id/wifi_list"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="72dp"
- android:layout_gravity="center_vertical|start"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="horizontal"
android:paddingStart="20dp"
- android:paddingEnd="40dp">
+ android:paddingEnd="24dp">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/wifi_icon"
android:layout_width="wrap_content"
@@ -52,31 +46,16 @@
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="72dp"
+ android:layout_marginEnd="30dp"
android:layout_weight="1"
- android:gravity="start|center_vertical"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:gravity="start|center_vertical">
<TextView
android:id="@+id/wifi_title"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="20dp"
- android:gravity="start|center_vertical"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:fontFamily="google-sans"
- android:layout_marginEnd="18dp"/>
+ style="@style/InternetDialog.NetworkTitle"
+ android:textSize="14sp"/>
<TextView
android:id="@+id/wifi_summary"
- android:textDirection="locale"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:fontFamily="google-sans"
- android:layout_marginEnd="18dp"/>
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
<FrameLayout
diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
index a21a63c..9cf09ff 100644
--- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
+++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
@@ -15,19 +15,25 @@
~ limitations under the License
-->
<!-- This is a view that shows a user switcher in Keyguard. -->
-<com.android.systemui.statusbar.phone.UserAvatarView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyguard_qs_user_switch_view"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- android:layout_centerHorizontal="true"
- android:layout_gravity="top|end"
- android:layout_marginEnd="16dp"
- systemui:avatarPadding="0dp"
- systemui:badgeDiameter="18dp"
- systemui:badgeMargin="1dp"
- systemui:frameColor="@color/kg_user_avatar_frame"
- systemui:framePadding="0dp"
- systemui:frameWidth="0dp">
-</com.android.systemui.statusbar.phone.UserAvatarView>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="end">
+ <com.android.systemui.statusbar.phone.UserAvatarView
+ android:id="@+id/kg_multi_user_avatar"
+ android:layout_width="@dimen/kg_framed_avatar_size"
+ android:layout_height="@dimen/kg_framed_avatar_size"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="16dp"
+ systemui:avatarPadding="0dp"
+ systemui:badgeDiameter="18dp"
+ systemui:badgeMargin="1dp"
+ systemui:frameColor="@color/kg_user_avatar_frame"
+ systemui:framePadding="0dp"
+ systemui:frameWidth="0dp">
+ </com.android.systemui.statusbar.phone.UserAvatarView>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index 3977635..921f788 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -14,11 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.SidefpsView
+<com.airbnb.lottie.LottieAnimationView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/sidefps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/accessibility_fingerprint_label">
-</com.android.systemui.biometrics.SidefpsView>
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/sidefps_animation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_rawRes="@raw/sfps_pulse"
+ android:contentDescription="@string/accessibility_fingerprint_label"/>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 687830d..0fcbfa1 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,7 +20,7 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="0.75"
+ systemui:sensorTouchAreaCoefficient="1.0"
android:contentDescription="@string/accessibility_fingerprint_label">
<ViewStub
diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json
new file mode 100644
index 0000000..c4903a2
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"Fingerprint Pulse Motion","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28,40,0],"to":[0.751,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/sfps_pulse_landscape.json b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
new file mode 100644
index 0000000..8c91762
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"Fingerprint Pulse Motion Portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 028f14a..85e2ee7 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Dit deblokkeer toegang vir alle programme en dienste wat toegelaat word om jou mikrofoon te gebruik."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dit deblokkeer toegang vir alle programme en dienste wat toegelaat word om jou kamera te gebruik."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dit deblokkeer toegang vir alle programme en dienste wat toegelaat word om jou kamera of mikrofoon te gebruik."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Toestel"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Gelaai"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laai tans"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Moenie teël byvoeg nie"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 798e0c3..4a6110b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ይህ የእርስዎን ማይክሮፎን እንዲጠቀሙ የተፈቀደላቸው የሁሉም መተግበሪያዎች እና አገልግሎቶች መዳረሻ እገዳን ያነሳል።"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ይህ ካሜራዎን እንዲጠቀሙ ለተፈቀደላቸው ሁሉም መተግበሪያዎች እና አገልግሎቶች መዳረሻን እገዳ ያነሳል።"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ይህ የእርስዎን ካሜራ ወይም ማይክሮፎን እንዲጠቀሙ የተፈቀደላቸው የሁሉም መተግበሪያዎች እና አገልግሎቶች መዳረሻ እገዳን ያነሳል።"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"መሣሪያ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ባትሪ ሞልቷል"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ኃይል በመሙላት ላይ"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</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">"ተገናኝቷል"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"የተንቀሳቃሽ ስልክ ውሂብ በራስ-ሰር አይገናኝም"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"ምንም አውታረ መረቦች የሉም"</string>
@@ -1180,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> የሚከተለውን ሰቅ ወደ ፈጣን ቅንብሮች ማከል ይፈልጋል"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ሰቅ አክል"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ሰቅ አታክል"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 6f5d73c..98ffae5 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -443,7 +443,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"يؤدي هذا الخيار إلى إزالة حظر الوصول بالنسبة إلى كل التطبيقات والخدمات المسموح لها باستخدام الميكروفون."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"يؤدي هذا الخيار إلى إزالة حظر الوصول بالنسبة إلى كل التطبيقات والخدمات المسموح لها باستخدام الكاميرا."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"يؤدي هذا الخيار إلى إزالة حظر الوصول بالنسبة إلى كل التطبيقات والخدمات المسموح لها باستخدام الكاميرا أو الميكروفون."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"الجهاز"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"تم الشحن"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"جارٍ الشحن"</string>
@@ -1203,4 +1203,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"يريد تطبيق <xliff:g id="APPNAME">%1$s</xliff:g> إضافة المربّع التالي إلى \"الإعدادات السريعة\""</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"إضافة مربّع"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"عدم إضافة مربّع"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as-land/strings.xml b/packages/SystemUI/res/values-as-land/strings.xml
index d5bf35a..6fa43cc 100644
--- a/packages/SystemUI/res/values-as-land/strings.xml
+++ b/packages/SystemUI/res/values-as-land/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="toast_rotation_locked" msgid="4914046305911646988">"স্ক্ৰীণখন এতিয়া লেণ্ডস্কেপ স্ক্ৰীণৰ দিশত লক কৰা অৱস্থাত আছে"</string>
+ <string name="toast_rotation_locked" msgid="4914046305911646988">"স্ক্ৰীনখন এতিয়া লেণ্ডস্কে\'প স্ক্ৰীনৰ দিশত লক কৰা অৱস্থাত আছে"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 614e4d8..fbf1fb4 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -74,8 +74,8 @@
<string name="usb_port_enabled" msgid="531823867664717018">"চাৰ্জাৰ আৰু আনুষংগিক সামগ্ৰী চিনাক্ত কৰিবলৈ USB প’ৰ্ট সক্ষম কৰা হ’ল"</string>
<string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"USB সক্ষম কৰক"</string>
<string name="learn_more" msgid="4690632085667273811">"অধিক জানক"</string>
- <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
- <string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
+ <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীন পূর্ণ কৰিবলৈ জুম কৰক"</string>
+ <string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীন পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্ৰীনশ্বট"</string>
<string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock অক্ষম কৰা হৈছে"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
@@ -99,7 +99,7 @@
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"সোঁফালৰ সীমা <xliff:g id="PERCENT">%1$d</xliff:g> শতাংশ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
- <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীণ ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
+ <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"ৰেকৰ্ড কৰা আৰম্ভ কৰিবনে?"</string>
<string name="screenrecord_description" msgid="1123231719680353736">"ৰেকৰ্ড কৰি থাকোঁতে, Android Systemএ আপোনাৰ স্ক্রীনত দৃশ্যমান হোৱা অথবা আপোনাৰ ডিভাইচত প্লে’ হৈ থকা যিকোনো সংবেনদশীল তথ্য কেপচাৰ কৰিব পাৰে। এইটোত পাছৱর্ড, পৰিশোধৰ তথ্য, ফট’, বার্তাসমূহ আৰু অডিঅ’ অন্তর্ভুক্ত হয়।"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"অডিঅ’ ৰেকৰ্ড কৰক"</string>
@@ -117,10 +117,10 @@
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ৰখোৱাৰ পৰা পুনৰ আৰম্ভ কৰক"</string>
<string name="screenrecord_cancel_label" msgid="7850926573274483294">"বাতিল কৰক"</string>
<string name="screenrecord_share_label" msgid="5025590804030086930">"শ্বেয়াৰ কৰক"</string>
- <string name="screenrecord_cancel_success" msgid="1775448688137393901">"স্ক্রীণ ৰেকৰ্ড কৰাটো বাতিল কৰা হ’ল"</string>
+ <string name="screenrecord_cancel_success" msgid="1775448688137393901">"স্ক্রীন ৰেকৰ্ড কৰাটো বাতিল কৰা হ’ল"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"স্ক্ৰীন ৰেকৰ্ডিং ছেভ কৰা হ’ল"</string>
<string name="screenrecord_save_text" msgid="3008973099800840163">"চাবলৈ টিপক"</string>
- <string name="screenrecord_delete_error" msgid="2870506119743013588">"স্ক্রীণ ৰেকৰ্ডিং মচি থাকোঁতে কিবা আসোঁৱাহ হ’ল"</string>
+ <string name="screenrecord_delete_error" msgid="2870506119743013588">"স্ক্রীন ৰেকৰ্ডিং মচি থাকোঁতে কিবা আসোঁৱাহ হ’ল"</string>
<string name="screenrecord_permission_error" msgid="7856841237023137686">"অনুমতি পাব পৰা নগ\'ল"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রীন ৰেকৰ্ড কৰা আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"ইউএছবিৰে ফাইল স্থানান্তৰণৰ বিকল্পসমূহ"</string>
@@ -131,7 +131,7 @@
<string name="accessibility_home" msgid="5430449841237966217">"গৃহ পৃষ্ঠাৰ বুটাম"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"মেনু"</string>
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"দিব্যাংগসকলৰ বাবে থকা সুবিধাসমূহ"</string>
- <string name="accessibility_rotate_button" msgid="1238584767612362586">"স্ক্ৰীণ ঘূৰাওক"</string>
+ <string name="accessibility_rotate_button" msgid="1238584767612362586">"স্ক্ৰীন ঘূৰাওক"</string>
<string name="accessibility_recent" msgid="901641734769533575">"অৱলোকন"</string>
<string name="accessibility_search_light" msgid="524741790416076988">"সন্ধান কৰক"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"কেমেৰা"</string>
@@ -190,7 +190,7 @@
<string name="face_dialog_looking_for_face" msgid="2656848512116189509">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"মুখমণ্ডলৰ আইকন"</string>
<string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"উপযোগিতা অনুসৰি জুম কৰা বুটাম।"</string>
- <string name="accessibility_compatibility_zoom_example" msgid="2617218726091234073">"স্ক্ৰীণৰ আকাৰ ডাঙৰ কৰিবলৈ জুম কৰক।"</string>
+ <string name="accessibility_compatibility_zoom_example" msgid="2617218726091234073">"স্ক্ৰীনৰ আকাৰ ডাঙৰ কৰিবলৈ জুম কৰক।"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ব্লুটুথ সংযোগ হ’ল।"</string>
<string name="accessibility_bluetooth_disconnected" msgid="7195823280221275929">"ব্লুটুথ সংযোগ বিচ্ছিন্ন কৰা হ’ল।"</string>
<string name="accessibility_no_battery" msgid="3789287732041910804">"বেটাৰি শেষ"</string>
@@ -249,10 +249,10 @@
<string name="accessibility_notification_dismissed" msgid="4411652015138892952">"জাননী অগ্ৰাহ্য কৰা হৈছে।"</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"জাননী পেনেল।"</string>
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"ক্ষিপ্ৰ ছেটিংসমূহ।"</string>
- <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীণ।"</string>
+ <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"বন্ধ স্ক্ৰীন।"</string>
<string name="accessibility_desc_settings" msgid="6728577365389151969">"ছেটিংসমূহ"</string>
<string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"অৱলোকন।"</string>
- <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীণ"</string>
+ <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীন"</string>
<string name="accessibility_desc_close" msgid="8293708213442107755">"বন্ধ কৰক"</string>
<string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
<string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"ৱাই-ফাই অফ কৰা হ’ল।"</string>
@@ -292,7 +292,7 @@
<string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"ৰং বিপৰীতকৰণ অন কৰা হ’ল।"</string>
<string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ম’বাইল হটস্পট অফ কৰা হ’ল।"</string>
<string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ম’বাইল হটস্পট অন কৰা হ’ল।"</string>
- <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"স্ক্ৰীণ কাষ্টিং বন্ধ কৰা হ’ল।"</string>
+ <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"স্ক্ৰীন কাষ্টিং বন্ধ কৰা হ’ল।"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="6422896967647049692">"কৰ্মস্থান ম’ড পজ হৈ আছে।"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"কৰ্মস্থান ম\'ড অন কৰা হ’ল।"</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ডেটা সঞ্চয়কাৰী সুবিধা অফ কৰা হ’ল।"</string>
@@ -320,14 +320,14 @@
<string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"জাননীৰ ছেটিংসমূহ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিংসমূহ"</string>
- <string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"আপোনাৰ ফ\'নৰ স্ক্ৰীণ স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string>
- <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"স্ক্ৰীণ লেণ্ডস্কেপ দিশত লক কৰা হ’ল।"</string>
- <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"স্ক্ৰীণ প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
- <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"আপোনাৰ ফ\'নৰ স্ক্ৰীণ এতিয়া স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string>
- <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"স্ক্ৰীণখন এতিয়া লেণ্ডস্কেইপ দিশত লক কৰা অৱস্থাত আছে।"</string>
- <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"স্ক্ৰীণখন এতিয়া প\'ৰ্ট্ৰেইট দিশত লক কৰা অৱস্থাত আছে।"</string>
+ <string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"আপোনাৰ ফ\'নৰ স্ক্ৰীন স্বয়ংক্ৰিয়ভাৱে ঘূৰিব।"</string>
+ <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"স্ক্ৰীন লেণ্ডস্কে\'প দিশত লক কৰা হ’ল।"</string>
+ <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"স্ক্ৰীন প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
+ <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"আপোনাৰ ফ\'নৰ স্ক্ৰীন এতিয়া স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string>
+ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"স্ক্ৰীনখন এতিয়া লেণ্ডস্কে\'প স্ক্ৰীনৰ দিশত লক কৰা অৱস্থাত আছে"</string>
+ <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"স্ক্ৰীনখন এতিয়া প\'ৰ্ট্ৰেইট দিশত লক কৰা অৱস্থাত আছে।"</string>
<string name="dessert_case" msgid="9104973640704357717">"মিষ্টান্ন ভাণ্ডাৰ"</string>
- <string name="start_dreams" msgid="9131802557946276718">"স্ক্ৰীণ ছেভাৰ"</string>
+ <string name="start_dreams" msgid="9131802557946276718">"স্ক্ৰীন ছেভাৰ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"ইথাৰনেট"</string>
<string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"অধিক বিকল্পৰ বাবে আইকনসমূহ স্পৰ্শ কৰি হেঁচি ধৰক"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"অসুবিধা নিদিব"</string>
@@ -376,7 +376,7 @@
<string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"ৱাই-ফাই অন হৈ আছে"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"কোনো ৱাই-ফাই নেটৱৰ্ক নাই"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"অন কৰি থকা হৈছে…"</string>
- <string name="quick_settings_cast_title" msgid="2279220930629235211">"স্ক্ৰীণ কাষ্ট"</string>
+ <string name="quick_settings_cast_title" msgid="2279220930629235211">"স্ক্ৰীন কাষ্ট"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"কাষ্টিং"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"নাম নথকা ডিভাইচ"</string>
<string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"কাষ্টৰ বাবে সাজু"</string>
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"এইটোৱে আপোনাৰ মাইক্ৰ\'ফ\'ন ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়া আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে এক্সেছ অৱৰোধৰ পৰা আঁতৰায়।"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"এইটোৱে আপোনাৰ কেমেৰা ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়া আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে এক্সেছ অৱৰোধৰ পৰা আঁতৰায়।"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"এইটোৱে আপোনাৰ কেমেৰা অথবা মাইক্ৰ\'ফ\'ন ব্যৱহাৰ কৰিবলৈ অনুমতি দিয়া আটাইবোৰ এপ্ আৰু সেৱাৰ বাবে এক্সেছ অৱৰোধৰ পৰা আঁতৰায়।"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ডিভাইচ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"চ্চার্জ হ’ল"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"চ্চার্জ হৈ আছে"</string>
@@ -464,7 +464,7 @@
<string name="phone_hint" msgid="6682125338461375925">"ফ\'নৰ বাবে আইকনৰপৰা ছোৱাইপ কৰক"</string>
<string name="voice_hint" msgid="7476017460191291417">"কণ্ঠধ্বনিৰে সহায়ৰ বাবে আইকনৰ পৰা ছোৱাইপ কৰক"</string>
<string name="camera_hint" msgid="4519495795000658637">"কেমেৰা খুলিবলৈ আইকনৰপৰা ছোৱাইপ কৰক"</string>
- <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"সম্পূর্ণ নিৰৱতা। এই কার্যই স্ক্ৰীণ ৰীডাৰসমূহকো নিৰৱ কৰিব।"</string>
+ <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"সম্পূর্ণ নীৰৱতা। এই কার্যই স্ক্ৰীন ৰীডাৰসমূহকো নীৰৱ কৰিব।"</string>
<string name="interruption_level_none" msgid="219484038314193379">"সম্পূর্ণ নিৰৱতা"</string>
<string name="interruption_level_priority" msgid="661294280016622209">"কেৱল গুৰুত্বপূৰ্ণ"</string>
<string name="interruption_level_alarms" msgid="2457850481335846959">"কেৱল এলাৰ্মসমূহ"</string>
@@ -710,7 +710,7 @@
<string name="tuner_full_importance_settings" msgid="1388025816553459059">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্ব"</string>
<string name="tuner_full_importance_settings_on" msgid="917981436602311547">"অন"</string>
<string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"অফ"</string>
- <string name="power_notification_controls_description" msgid="1334963837572708952">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্বৰ সৈতে আপুনি এটা এপৰ জাননীৰ গুৰুত্বৰ স্তৰ ০ৰ পৰা ৫লৈ ছেট কৰিব পাৰে।\n\n"<b>"স্তৰ ৫"</b>" \n- জাননী তালিকাৰ একেবাৰে ওপৰত দেখুৱাওক \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ অনুমতি দিয়ক\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৪"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৩"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n\n"<b>"স্তৰ ২"</b>" \n- সম্পূর্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব \n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n- কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব\n\n"<b>" স্তৰ ১"</b>" \n- সম্পূৰ্ণ স্ক্ৰীণত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n-কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব \n- লক স্ক্ৰীণ আৰু স্থিতি দণ্ডৰ পৰা লুকুৱাই ৰাখক \n- জাননী তালিকাৰ একেবাৰে তলত দেখুৱাওক\n\n"<b>"স্তৰ ০"</b>" \n- এই এপৰ সকলো জাননী অৱৰোধ কৰক"</string>
+ <string name="power_notification_controls_description" msgid="1334963837572708952">"জাননী নিয়ন্ত্ৰণৰ অধিক কৰ্তৃত্বৰ সৈতে আপুনি এটা এপৰ জাননীৰ গুৰুত্বৰ স্তৰ ০ৰ পৰা ৫লৈ ছেট কৰিব পাৰে।\n\n"<b>"স্তৰ ৫"</b>" \n- জাননী তালিকাৰ একেবাৰে ওপৰত দেখুৱাওক \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ অনুমতি দিয়ক\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৪"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- সদায় ভুমুকি মাৰিবলৈ দিয়ক\n\n"<b>"স্তৰ ৩"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n\n"<b>"স্তৰ ২"</b>" \n- সম্পূর্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব \n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n- কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব\n\n"<b>" স্তৰ ১"</b>" \n- সম্পূৰ্ণ স্ক্ৰীনত থাকোঁতে ব্যাঘাত জন্মাবলৈ নিদিব\n- কেতিয়াও ভুমুকি মাৰিবলৈ নিদিব\n-কেতিয়াও শব্দ আৰু কম্পন কৰিবলৈ নিদিব \n- লক স্ক্ৰীন আৰু স্থিতি দণ্ডৰ পৰা লুকুৱাই ৰাখক \n- জাননী তালিকাৰ একেবাৰে তলত দেখুৱাওক\n\n"<b>"স্তৰ ০"</b>" \n- এই এপৰ আটাইবোৰ জাননী অৱৰোধ কৰক"</string>
<string name="notification_header_default_channel" msgid="225454696914642444">"জাননীসমূহ"</string>
<string name="notification_channel_disabled" msgid="928065923928416337">"আপোনাক এই জাননীসমূহ আৰু দেখুওৱা নহ’ব"</string>
<string name="notification_channel_minimized" msgid="6892672757877552959">"এই জাননীসমূহ মিনিমাইজ কৰি থোৱা হ\'ব"</string>
@@ -758,11 +758,11 @@
<string name="see_more_title" msgid="7409317011708185729">"অধিক চাওক"</string>
<string name="appops_camera" msgid="5215967620896725715">"এই এপে কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="appops_microphone" msgid="8805468338613070149">"এই এপে মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
- <string name="appops_overlay" msgid="4822261562576558490">"এই এপটো আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে।"</string>
+ <string name="appops_overlay" msgid="4822261562576558490">"এই এপটো আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ আছে।"</string>
<string name="appops_camera_mic" msgid="7032239823944420431">"এই এপে মাইক্ৰ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
- <string name="appops_camera_overlay" msgid="6466845606058816484">"এই এপে আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
- <string name="appops_mic_overlay" msgid="4609326508944233061">"এই এপে আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
- <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই এপে আপোনাৰ স্ক্ৰীণত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
+ <string name="appops_camera_overlay" msgid="6466845606058816484">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
+ <string name="appops_mic_overlay" msgid="4609326508944233061">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string>
+ <string name="appops_camera_mic_overlay" msgid="5584311236445644095">"এই এপে আপোনাৰ স্ক্ৰীনত থকা অন্য় এপৰ ওপৰত প্ৰদৰ্শিত হৈ মাইক্ৰ\'ফ\'ন আৰু কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string>
<string name="notification_appops_settings" msgid="5208974858340445174">"ছেটিংসমূহ"</string>
<string name="notification_appops_ok" msgid="2177609375872784124">"ঠিক আছে"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া <b>গুৰুত্ব ডিফ’ল্ট</b>লৈ বৃদ্ধি কৰিছে।"</string>
@@ -935,7 +935,7 @@
<string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাৱাৰ মেনু"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
- <string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীণ"</string>
+ <string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীন"</string>
<string name="thermal_shutdown_title" msgid="2702966892682930264">"আপোনাৰ ফ\'নটো গৰম হোৱাৰ কাৰণে অফ কৰা হৈছিল"</string>
<string name="thermal_shutdown_message" msgid="6142269839066172984">"আপোনাৰ ফ’নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\nঅধিক তথ্যৰ বাবে টিপক"</string>
<string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"আপোনাৰ ফ\'নটো অত্যধিক গৰম হোৱাৰ বাবে ইয়াক ঠাণ্ডা কৰিবলৈ অফ কৰা হৈছিল। আপোনাৰ ফ\'নটো এতিয়া স্বাভাৱিকভাৱে চলি আছে।\n\nআপোনাৰ ফ\'নটো গৰম হ\'ব পাৰে, যদিহে আপুনি:\n • ফ\'নটোৰ হাৰ্ডৱেৰ অত্যধিক মাত্ৰাত ব্যৱহাৰ কৰা এপসমূহ চলালে (যেনে, ভিডিঅ\' গেইম, ভিডিঅ\', দিক্-নিৰ্দেশনা এপসমূহ)\n • খুউব ডাঙৰ আকাৰৰ ফাইল আপল\'ড বা ডাউনল’ড কৰিলে\n • আপোনাৰ ফ\'নটো উচ্চ তাপমাত্ৰাৰ পৰিৱেশত ব্যৱহাৰ কৰিলে"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</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">"সংযোজিত হৈ আছে"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"ম’বাইল ডেটা স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>এ ক্ষিপ্ৰ ছেটিঙত এই টাইলটো যোগ দিব বিচাৰিছে"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ দিয়ক"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ নিদিব"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7cd3b32..44e6474 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Kamera və mikrofon istifadə edən bütün tətbiq və xidmətlərə giriş verir."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera və mikrofon istifadə edən bütün tətbiq və xidmətlərə giriş verir."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera və mikrofon istifadə edən bütün tətbiq və xidmətlərə giriş verir."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Enerji yığılıb"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Enerji doldurulur"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdakı mozaiki Sürətli Ayarlara əlavə etmək istəyir"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik əlavə edin"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mozaik əlavə etməyin"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f155b76..b40f48d 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ovim će se odblokirati pristup za sve aplikacije i usluge koje imaju dozvolu za korišćenje mikrofona."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ovim će se odblokirati pristup za sve aplikacije i usluge koje imaju dozvolu za korišćenje kamere."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ovim će se odblokirati pristup za sve aplikacije i usluge koje imaju dozvolu za korišćenje kamere ili mikrofona."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjena je"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Puni se"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi da doda sledeću pločicu u Brza podešavanja"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj pločicu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index ab3940b..29f46ba 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Доступ адкрыецца для ўсіх праграм і сэрвісаў, якім дазволена выкарыстоўваць мікрафон."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Доступ адкрыецца для ўсіх праграм і сэрвісаў, якім дазволена выкарыстоўваць камеру."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Доступ адкрыецца для ўсіх праграм і сэрвісаў, якім дазволена выкарыстоўваць камеру ці мікрафон."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Прылада"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Зараджаны"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарадка"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> запытвае дазвол на дадаванне ў Хуткія налады наступнай пліткі"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Дадаць плітку"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не дадаваць плітку"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2d698ee..43f4bd7 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Това действие отблокира достъпа за всички приложения и услуги, които имат разрешение да използват микрофона ви."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Това действие отблокира достъпа за всички приложения и услуги, които имат разрешение да използват камерата ви."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Това действие отблокира достъпа за всички приложения и услуги, които имат разрешение да използват камерата или микрофона ви."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Заредена"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарежда се"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> иска да добави следния панел към бързите настройки"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавяне на панел"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Отмяна на добавянето"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 46172bd..05cbfc0 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"এটার জন্য মাইক্রোফোনের অ্যাক্সেস সেই সব অ্যাপ এবং পরিষেবার জন্য আনব্লক হয়ে যাবে, যাতে আপনার মাইক্রোফোন ব্যবহার করার অনুমতি দেওয়া হয়েছে।"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"এটার জন্য ক্যামেরার অ্যাক্সেস সেই সব অ্যাপ এবং পরিষেবার জন্য আনব্লক হয়ে যাবে, যাতে আপনার ক্যামেরা ব্যবহারের অনুমতি দেওয়া হয়েছে।"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"এটার জন্য ক্যামেরা অথবা মাইক্রোফোনের অ্যাক্সেস সেই সব অ্যাপ এবং পরিষেবার জন্য আনব্লক হয়ে যাবে, যাতে আপনার ক্যামেরা অথবা মাইক্রোফোন ব্যবহারের অনুমতি দেওয়া হয়েছে।"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ডিভাইস"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"চার্জ হয়েছে"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"চার্জ হচ্ছে"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</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">"কানেক্ট করা আছে"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"মোবাইল ডেটা নিজে থেকে কানেক্ট হবে না"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
@@ -1180,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> নিম্নলিখিত টাইল দ্রুত সেটিংস মেনুতে যোগ করতে চায়"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ করুন"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ করবেন না"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index d6a6602..81b0274 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ovim se deblokira pristup za sve aplikacije i usluge kojima je dozvoljeno da koriste vaš mikrofon."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ovim se deblokira pristup za sve aplikacije i usluge kojima je dozvoljeno da koriste vašu kameru."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ovim se deblokira pristup za sve aplikacije i usluge kojima je dozvoljeno da koriste vašu kameru ili mikrofon."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjeno"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Punjenje"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati karticu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ea91dc1..59fef13 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Aquesta opció desbloqueja l\'accés de tots els serveis i aplicacions que tenen permís per utilitzar el micròfon."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Aquesta opció desbloqueja l\'accés de tots els serveis i aplicacions que tenen permís per utilitzar la càmera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Aquesta opció desbloqueja l\'accés de tots els serveis i aplicacions que tenen permís per utilitzar la càmera o el micròfon."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositiu"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"S\'està carregant"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vol afegir la icona següent a la configuració ràpida"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Afegeix la icona"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No afegeixis la icona"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c9021f8..b04559a 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tímto odblokujete přístup všem aplikacím a službám, které mají povoleno používat váš mikrofon."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tímto odblokujete přístup všem aplikacím a službám, které mají povoleno používat váš fotoaparát."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tímto odblokujete přístup všem aplikacím a službám, které mají povoleno používat váš fotoaparát či mikrofon."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Zařízení"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Nabito"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nabíjení"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> chce do Rychlého nastavení přidat následující dlaždici"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Přidat dlaždici"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepřidávat dlaždici"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c7214ff..afe693a 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -275,10 +275,10 @@
<string name="accessibility_quick_settings_bluetooth_connected" msgid="5237625393869747261">"Der er oprettet forbindelse til Bluetooth."</string>
<string name="accessibility_quick_settings_bluetooth_changed_off" msgid="3344226652293797283">"Bluetooth er slået fra."</string>
<string name="accessibility_quick_settings_bluetooth_changed_on" msgid="1263282011749437549">"Bluetooth er slået til."</string>
- <string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"Placeringsrapportering er slået fra."</string>
- <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"Placeringsrapportering er slået til."</string>
- <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Placeringsrapportering er slået fra."</string>
- <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Placeringsrapportering er slået til."</string>
+ <string name="accessibility_quick_settings_location_off" msgid="6122523378294740598">"Lokationsrapportering er slået fra."</string>
+ <string name="accessibility_quick_settings_location_on" msgid="6869947200325467243">"Lokationsrapportering er slået til."</string>
+ <string name="accessibility_quick_settings_location_changed_off" msgid="5132776369388699133">"Lokationsrapportering er slået fra."</string>
+ <string name="accessibility_quick_settings_location_changed_on" msgid="7159115433070112154">"Lokationsrapportering er slået til."</string>
<string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Alarmen er indstillet til <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_close" msgid="2974895537860082341">"Luk panelet."</string>
<string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Mere tid."</string>
@@ -309,7 +309,7 @@
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Genoptag"</string>
<string name="gps_notification_searching_text" msgid="231304732649348313">"Søger efter GPS"</string>
<string name="gps_notification_found_text" msgid="3145873880174658526">"Placeringen er angivet ved hjælp af GPS"</string>
- <string name="accessibility_location_active" msgid="2845747916764660369">"Aktive placeringsanmodninger"</string>
+ <string name="accessibility_location_active" msgid="2845747916764660369">"Aktive lokationsanmodninger"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensorer er slået fra"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Ryd alle notifikationer."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> mere"</string>
@@ -352,8 +352,8 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"Stående"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Liggende"</string>
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Inputmetode"</string>
- <string name="quick_settings_location_label" msgid="2621868789013389163">"Placering"</string>
- <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Placering fra"</string>
+ <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
+ <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Lokation fra"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraadgang"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonadgang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgængelig"</string>
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Dette fjerner adgangsblokeringen for alle apps og tjenester, der har tilladelse til at bruge din mikrofon."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dette fjerner adgangsblokeringen for alle apps og tjenester, der har tilladelse til at bruge dit kamera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dette fjerner adgangsblokeringen for alle apps og tjenester, der har tilladelse til at bruge dit kamera eller din mikrofon."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhed"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Opladet"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Oplader"</string>
@@ -555,9 +555,9 @@
<string name="disconnect_vpn" msgid="26286850045344557">"Afbryd VPN-forbindelse"</string>
<string name="monitoring_button_view_policies" msgid="3869724835853502410">"Se politikker"</string>
<string name="monitoring_button_view_controls" msgid="8316440345340701117">"Se indstillinger"</string>
- <string name="monitoring_description_named_management" msgid="505833016545056036">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds placeringsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
+ <string name="monitoring_description_named_management" msgid="505833016545056036">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds lokationsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
<string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> kan muligvis administrere apps, få adgang til data, der er tilknyttet denne enhed, og ændre enhedens indstillinger.\n\nKontakt <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>, hvis du har spørgsmål."</string>
- <string name="monitoring_description_management" msgid="4308879039175729014">"Denne enhed tilhører din organisation.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds placeringsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
+ <string name="monitoring_description_management" msgid="4308879039175729014">"Denne enhed tilhører din organisation.\n\nDin it-administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps, data, der er tilknyttet din enhed, og din enheds lokationsdata.\n\nKontakt din it-administrator for at få mere at vide."</string>
<string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Din organisation har installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Din organisation har installeret et nøglecenter på din arbejdsprofil. Din sikre netværkstrafik kan overvåges eller ændres."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Der er installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string>
@@ -569,7 +569,7 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Din personlige profil har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string>
<string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"Din enhed administreres af <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
<string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> bruger <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> til at administrere din enhed."</string>
- <string name="monitoring_description_do_body" msgid="7700878065625769970">"Din administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps og data, der er knyttet til denne enhed, samt enhedens placeringsoplysninger."</string>
+ <string name="monitoring_description_do_body" msgid="7700878065625769970">"Din administrator kan overvåge og administrere indstillinger, virksomhedsadgang, apps og data, der er knyttet til denne enhed, samt enhedens lokationsoplysninger."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
<string name="monitoring_description_do_learn_more" msgid="645149183455573790">"Få flere oplysninger"</string>
<string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"Du har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. e-mails, apps og websites."</string>
@@ -580,7 +580,7 @@
<string name="monitoring_description_network_logging" msgid="577305979174002252">"Din administrator har aktiveret netværksregistrering, som overvåger trafik på din enhed.\n\nKontakt din administrator for at få flere oplysninger."</string>
<string name="monitoring_description_vpn" msgid="1685428000684586870">"Du gav en app tilladelse til at konfigurere en VPN-forbindelse.\n\nDenne app kan overvåge din enhed og netværksaktivitet, bl.a. e-mails, apps og websites."</string>
<string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"Din arbejdsprofil administreres af <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nDin administrator kan overvåge din netværksaktivitet, bl.a. e-mails, apps og websites.\n\nKontakt din administrator for at få flere oplysninger.\n\nDu har også forbindelse til et VPN, som kan overvåge din netværksaktivitet."</string>
- <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enhed administreres af din forælder. Din forælder kan se og administrere oplysninger såsom de apps, du bruger, din placering og din skærmtid."</string>
+ <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enhed administreres af din forælder. Din forælder kan se og administrere oplysninger såsom de apps, du bruger, din lokation og din skærmtid."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="monitoring_description_app" msgid="376868879287922929">"Du har forbindelse til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string>
<string name="monitoring_description_app_personal" msgid="1970094872688265987">"Du har forbindelse til <xliff:g id="APPLICATION">%1$s</xliff:g>, som kan overvåge din private netværksaktivitet, bl.a. e-mails, apps og websites."</string>
@@ -916,8 +916,8 @@
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt felt"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tilføj felt"</string>
<string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til lokation <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Lokation <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Feltet blev tilføjet"</string>
<string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Feltet blev fjernet"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
@@ -1015,7 +1015,7 @@
<string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonopkald"</string>
<string name="ongoing_privacy_dialog_attribution_text" msgid="4738795925380373994">"(via <xliff:g id="APPLICATION_NAME_S_">%s</xliff:g>)"</string>
<string name="privacy_type_camera" msgid="7974051382167078332">"kameraet"</string>
- <string name="privacy_type_location" msgid="7991481648444066703">"placering"</string>
+ <string name="privacy_type_location" msgid="7991481648444066703">"lokation"</string>
<string name="privacy_type_microphone" msgid="9136763906797732428">"mikrofonen"</string>
<string name="sensor_privacy_mode" msgid="4462866919026513692">"Deaktiver sensorer"</string>
<string name="device_services" msgid="1549944177856658705">"Enhedstjenester"</string>
@@ -1133,8 +1133,8 @@
<string name="upcoming_birthday_status_content_description" msgid="2165036816803797148">"<xliff:g id="NAME">%1$s</xliff:g> har snart fødselsdag"</string>
<string name="anniversary_status" msgid="1790034157507590838">"Årsdag"</string>
<string name="anniversary_status_content_description" msgid="8212171790843327442">"<xliff:g id="NAME">%1$s</xliff:g> har jubilæum i dag"</string>
- <string name="location_status" msgid="1294990572202541812">"Deler placering"</string>
- <string name="location_status_content_description" msgid="2982386178160071305">"<xliff:g id="NAME">%1$s</xliff:g> deler sin placering"</string>
+ <string name="location_status" msgid="1294990572202541812">"Deler lokation"</string>
+ <string name="location_status_content_description" msgid="2982386178160071305">"<xliff:g id="NAME">%1$s</xliff:g> deler sin lokation"</string>
<string name="new_story_status" msgid="9012195158584846525">"Ny historie"</string>
<string name="new_story_status_content_description" msgid="4963137422622516708">"<xliff:g id="NAME">%1$s</xliff:g> har delt en ny historie"</string>
<string name="video_status" msgid="4548544654316843225">"Ser"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil gerne føje dette handlingsfelt til Kvikmenu"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tilføj handlingsfelt"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tilføj ikke felt"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 6b5281e..20e967d 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Dadurch wird die Blockierung des Zugriffs für alle Apps und Dienste aufgehoben, die dein Mikrofon verwenden dürfen."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dadurch wird die Blockierung des Zugriffs für alle Apps und Dienste aufgehoben, die deine Kamera verwenden dürfen."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dadurch wird die Blockierung des Zugriffs für alle Apps und Dienste aufgehoben, die deine Kamera oder dein Mikrofon verwenden dürfen."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Gerät"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Aufgeladen"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Wird aufgeladen"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</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">"Verbunden"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Keine automatische Verbindung über mobile Daten"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Keine Netzwerke verfügbar"</string>
@@ -1180,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> möchte die folgende Kachel den Schnelleinstellungen hinzufügen"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kachel hinzufügen"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kachel nicht hinzu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b55256c..5a11e3d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Με αυτόν τον τρόπο καταργείται ο αποκλεισμός της πρόσβασης για όλες τις εφαρμογές και υπηρεσίες που επιτρέπεται να χρησιμοποιούν το μικρόφωνό σας."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Με αυτόν τον τρόπο καταργείται ο αποκλεισμός της πρόσβασης για όλες τις εφαρμογές και υπηρεσίες που επιτρέπεται να χρησιμοποιούν την κάμερά σας."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Με αυτόν τον τρόπο καταργείται ο αποκλεισμός της πρόσβασης για όλες τις εφαρμογές και υπηρεσίες που επιτρέπεται να χρησιμοποιούν την κάμερα ή το μικρόφωνό σας."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Συσκευή"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Φορτίστηκε"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Φόρτιση"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> θέλει να προσθέσει το παρακάτω πλακίδιο στις Γρήγορες ρυθμίσεις"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Προσθήκη πλακιδίου"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Χωρίς προσθ. πλακιδ."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 081bfcb..8e9c550 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"This unblocks access for all apps and services allowed to use your microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 4e32027..9d8008b 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"This unblocks access for all apps and services allowed to use your microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 081bfcb..8e9c550 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"This unblocks access for all apps and services allowed to use your microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 081bfcb..8e9c550 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"This unblocks access for all apps and services allowed to use your microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 6641705..fd5faaf 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"This unblocks access for all apps and services allowed to use your microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"This unblocks access for all apps and services allowed to use your camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"This unblocks access for all apps and services allowed to use your camera or microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Charged"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charging"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 154248c..335a66b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Esta acción desbloquea el acceso para todos los servicios y las apps que tengan permitido usar el micrófono."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Esta acción desbloquea el acceso para todos los servicios y las apps que tengan permitido usar la cámara."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Esta acción permite que todas las aplicaciones y servicios que tengan permiso puedan usar la cámara o el micrófono."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere agregar el siguiente azulejo a la Configuración rápida"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Agregar azulejo"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No agregar azulejo"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 8e19028..b5cb7c4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Si lo haces, todas las aplicaciones y servicios que tengan permiso podrán usar tu micrófono."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Si lo haces, todas las aplicaciones y servicios que tengan permiso podrán usar tu cámara."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Si lo haces, todas las aplicaciones y servicios que tengan permiso podrán usar tu cámara o tu micrófono."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere añadir el siguiente recuadro a ajustes rápidos"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Añadir recuadro"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No añadir recuadro"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d603182..3bcf351 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Sellega tühistatakse juurdepääsu blokeerimine kõikide rakenduste ja teenuste puhul, millel on lubatud mikrofoni kasutada."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Sellega tühistatakse juurdepääsu blokeerimine kõikide rakenduste ja teenuste puhul, millel on lubatud kaamerat kasutada."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Sellega tühistatakse juurdepääsu blokeerimine kõikide rakenduste ja teenuste puhul, millel on lubatud kaamerat või mikrofoni kasutada."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Seade"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Laetud"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laadimine"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> soovib kiirseadetesse lisada järgmise paani"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisa paan"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ära lisa paani"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index d59dc7a..5003b4e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -305,7 +305,7 @@
<string name="data_usage_disabled_dialog_4g_title" msgid="1490779000057752281">"4G datuen erabilera pausatu da"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datu-konexioa pausatu egin da"</string>
<string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Datuen erabilera pausatu da"</string>
- <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Iritsi zara ezarri zenuen datu-mugara. Datu-konexioa erabiltzeari utzi diozu.\n\nDatu-konexioa erabiltzeari berrekiten badiozu, datuen erabileragatiko gastuak izango dituzu."</string>
+ <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Iritsi zara ezarri zenuen datu-mugara. Datu-konexioa erabiltzeari utzi diozu.\n\nDatu-konexioa erabiltzeari berrekiten badiozu, baliteke zerbait ordaindu behar izatea datuak erabiltzeagatik."</string>
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Jarraitu erabiltzen"</string>
<string name="gps_notification_searching_text" msgid="231304732649348313">"GPS seinalearen bila"</string>
<string name="gps_notification_found_text" msgid="3145873880174658526">"Kokapena GPS bidez ezarri da"</string>
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Mikrofonoa atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamera atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dute."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera edo mikrofonoa atzitzeko baimena duten aplikazio eta zerbitzu guztiek erabili ahalko dituzte."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Gailua"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Kargatuta"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Kargatzen"</string>
@@ -999,7 +999,7 @@
<string name="slice_permission_deny" msgid="6870256451658176895">"Ukatu"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"Sakatu bateria-aurrezlea noiz aktibatu programatzeko"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktibatu aurrezlea bateria agortzeko arriskua dagoenean"</string>
- <string name="no_auto_saver_action" msgid="7467924389609773835">"Ez"</string>
+ <string name="no_auto_saver_action" msgid="7467924389609773835">"Ez, eskerrik asko"</string>
<string name="auto_saver_enabled_title" msgid="4294726198280286333">"Bateria-aurrezlea aktibatu da"</string>
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Bateria-aurrezlea automatikoki aktibatuko da bateriaren %% <xliff:g id="PERCENTAGE">%d</xliff:g> gelditzen denean."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Ezarpenak"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak lauza hau gehitu nahi du Ezarpen bizkorrak menuan:"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Gehitu lauza"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ez gehitu lauza"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 62281ef..abe01d5 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"با این کار دسترسی برای همه برنامهها و سرویسهایی که مجاز هستند از میکروفونتان استفاده کنند لغو انسداد میشود."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"با این کار دسترسی برای همه برنامهها و سرویسهایی که مجاز هستند از دوربینتان استفاده کنند لغو انسداد میشود."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"با این کار دسترسی برای همه برنامهها و دستگاههایی که مجاز هستند از دوربین یا میکروفونتان استفاده کنند لغو انسداد میشود."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"دستگاه"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"شارژ کامل شد"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"در حال شارژ شدن"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> میخواهد کاشی زیر را به «تنظیمات فوری» اضافه کند"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"افزودن کاشی"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"کاشی اضافه نشود"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 629b1ff..db11c26 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tämä kumoaa kaikkien sellaisten sovellusten ja palveluiden eston, joilla on lupa käyttää mikrofoniasi."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tämä kumoaa kaikkien sellaisten sovellusten ja palveluiden eston, joilla on lupa käyttää kameraasi."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tämä kumoaa eston kaikkien sellaisten sovellusten ja palveluiden osalta, joilla on lupa käyttää kameraasi tai mikrofoniasi."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Laite"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Ladattu"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Ladataan"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> haluaa lisätä seuraavan laatan pika-asetuksiin"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisää laatta"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Älä lisää laattaa"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index a13f849..35ca25a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Cette action débloque l\'accès pour toutes les applications et tous les services autorisés à utiliser le microphone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Cette action débloque l\'accès pour toutes les applications et pour tous les services autorisés à utiliser l\'appareil photo."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Cette action débloque l\'accès pour toutes les applications et tous les services autorisés à utiliser l\'appareil photo ou le microphone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Appareil"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Chargée"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Charge en cours..."</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</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">"Connexion active"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Aucune connexion auto. des données cellulaires"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau n\'est accessible"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"L\'application <xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter la tuile suivante au menu Paramètres rapides"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter la tuile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter tuile"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index f0bc8be..47b1b45 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Cette action débloque l\'accès à tous les services et applis autorisés à utiliser votre micro."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Cette action débloque l\'accès à tous les services et applis autorisés à utiliser votre appareil photo."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Cette action débloque l\'accès pour tous les services et applis autorisés à utiliser votre appareil photo ou votre micro."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Appareil"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'aperçu"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Chargé"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"En charge"</string>
@@ -1179,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter le bloc suivant aux Réglages rapides"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter un bloc"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter bloc"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d6f8036..b36173d 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Con esta acción desbloquearase o acceso ao micrófono para todas as aplicacións e servizos que teñan permiso para utilizalo."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Con esta acción desbloquearase o acceso á cámara para todas as aplicacións e servizos que teñan permiso para utilizala."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Con esta acción desbloquearase o acceso á cámara ou ao micrófono para todas as aplicacións e servizos que teñan permiso para utilizalos."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Cargado"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Cargando"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</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">"Conectada"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Os datos móbiles non se conectarán automaticamente"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Sen conexión"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Non hai outras redes dispoñibles"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Non hai redes dispoñibles"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> solicita a túa aprobación para engadir o seguinte atallo a Configuración rápida"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engadir atallo"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non engadir atallo"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 11fe9b9..d51cce9 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -147,7 +147,7 @@
<string name="accessibility_manage_notification" msgid="582215815790143983">"નોટિફિકેશનને મેનેજ કરો"</string>
<string name="phone_label" msgid="5715229948920451352">"ફોન ખોલો"</string>
<string name="voice_assist_label" msgid="3725967093735929020">"વૉઇસ સહાય ખોલો"</string>
- <string name="camera_label" msgid="8253821920931143699">"કૅમેરો ખોલો"</string>
+ <string name="camera_label" msgid="8253821920931143699">"કૅમેરા ખોલો"</string>
<string name="cancel" msgid="1089011503403416730">"રદ કરો"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"કન્ફર્મ કરો"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ફરી પ્રયાસ કરો"</string>
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"આ તમારા માઇક્રોફોનનો ઉપયોગ કરવાની મંજૂરી ધરાવતી તમામ ઍપ અને સેવાઓ માટે ઍક્સેસને અનબ્લૉક કરે છે."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"આ તમારા કૅમેરાનો ઉપયોગ કરવાની મંજૂરી ધરાવતી તમામ ઍપ અને સેવાઓ માટે ઍક્સેસને અનબ્લૉક કરે છે."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"આ તમારા કૅમેરા અથવા માઇક્રોફોનનો ઉપયોગ કરવાની મંજૂરી ધરાવતી તમામ ઍપ અને સેવાઓ માટે ઍક્સેસને અનબ્લૉક કરે છે."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ડિવાઇસ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ચાર્જ થઈ ગયું"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ચાર્જ થઈ રહ્યું છે"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</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">"કનેક્ટ કરેલું"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"મોબાઇલ ડેટા ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ઝડપી સેટિંગમાં <xliff:g id="APPNAME">%1$s</xliff:g> નીચે જણાવેલા ટાઇલ ઉમેરવા માગે છે"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 44100dc..1726b81 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ऐसा करने से, माइक्रोफ़ोन का ऐक्सेस उन सभी ऐप्लिकेशन और सेवाओं के लिए अनब्लॉक हो जाएगा जिन्हें माइक्रोफ़ोन का इस्तेमाल करने की अनुमति दी गई है."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ऐसा करने से, कैमरे का ऐक्सेस उन सभी ऐप्लिकेशन और सेवाओं के लिए अनब्लॉक हो जाएगा जिन्हें कैमरे का इस्तेमाल करने की अनुमति दी गई है."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ऐसा करने से, कैमरा या माइक्रोफ़ोन का ऐक्सेस उन सभी ऐप्लिकेशन और सेवाओं के लिए अनब्लॉक हो जाएगा जिन्हें ये इस्तेमाल करने की अनुमति है."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"डिवाइस"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"चार्ज हो गई है"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हो रही है"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> इस टाइल को \'फटाफट सेटिंग\' में जोड़ने के लिए अनुमति चाहता है"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोड़ें"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल न जोड़ें"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index ed1f3cb..2a69589 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Time se deblokira pristup za sve aplikacije i usluge kojima je dopuštena upotreba vašeg mikrofona."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Time se deblokira pristup za sve aplikacije i usluge kojima je dopuštena upotreba vašeg fotoaparata."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Time se deblokira pristup za sve aplikacije i usluge kojima je dopuštena upotreba vašeg fotoaparata ili mikrofona."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Uređaj"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Napunjeno"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Punjenje"</string>
@@ -1171,7 +1171,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</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">"Povezano"</string>
- <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilni podaci neće se automatski povezati"</string>
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobilna veza neće se automatski uspostaviti"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću pločicu u Brze postavke"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati pločicu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index b47e75b..d964611 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ezzel feloldja a hozzáférés letiltását az összes olyan alkalmazás és szolgáltatás esetében, amelyek számára engedélyezte a mikrofon használatát."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ezzel feloldja a hozzáférés letiltását az összes olyan alkalmazás és szolgáltatás esetében, amelyek számára engedélyezte a kamera használatát."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ezzel feloldja a hozzáférés letiltását az összes olyan alkalmazás és szolgáltatás esetében, amelyek számára engedélyezte a kamera vagy a mikrofon használatát."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Eszköz"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Feltöltve"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Töltés"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> a következő mozaikot szeretné hozzáadni a Gyorsbeállításokhoz"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik hozzáadása"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne legyen hozzáadva"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 0fcfd65..2372265 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Սա բացում է մուտքը բոլոր հավելվածների և ծառայությունների համար, որոնք ունեն ձեր խոսափողն օգտագործելու թույլտվություն։"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Սա բացում է մուտքը բոլոր հավելվածների և ծառայությունների համար, որոնք ունեն ձեր տեսախցիկն օգտագործելու թույլտվություն։"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Սա բացում է մուտքը բոլոր հավելվածների և ծառայությունների համար, որոնք ունեն ձեր տեսախցիկը կամ խոսափողն օգտագործելու թույլտվություն։"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Սարք"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Լիցքավորված է"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Լիցքավորվում է"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածն ուզում է ավելացնել հետևյալ սալիկը Արագ կարգավորումներում"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ավելացնել սալիկ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Չավելացնել սալիկ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 6486b0b..7de4c70 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan mikrofon."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Langkah ini akan berhenti memblokir akses untuk semua aplikasi dan layanan yang diizinkan menggunakan kamera atau mikrofon."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Perangkat"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Terisi penuh"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Mengisi daya"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingin menambahkan kartu berikut ke Setelan Cepat"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan kartu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah kartu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 0af672c..9805fe9 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Þetta veitir öllum forritum og þjónustum aðgang að hljóðnemanum þínum."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Þetta veitir öllum forritum og þjónustum aðgang að myndavélinni þinni."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Þetta veitir öllum forritum og þjónustum aðgang að myndavélinni og hljóðnemanum þínum."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Tæki"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Fullhlaðin"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Í hleðslu"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill bæta eftirfarandi reit við flýtistillingar"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Bæta reit við"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ekki bæta reit við"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index d2f6335..e97a9a5 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Viene sbloccato l\'accesso per tutti i servizi e le app autorizzati a usare il microfono."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Viene sbloccato l\'accesso per tutti i servizi e le app autorizzati a usare la fotocamera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Viene sbloccato l\'accesso per tutti i servizi e le app autorizzati a usare la fotocamera o il microfono."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carica"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"In carica"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vuole aggiungere il seguente riquadro alle Impostazioni rapide"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Aggiungi riquadro"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non aggiungerlo"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 78370d6..ae5dc08 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"הפעולה הזו מבטלת את חסימת הגישה של כל האפליקציות והשירותים שמורשים להשתמש במיקרופון."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"הפעולה הזו מבטלת את חסימת הגישה של כל האפליקציות והשירותים שמורשים להשתמש במצלמה."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"הפעולה הזו מבטלת את חסימת הגישה של כל האפליקציות והשירותים שמורשים להשתמש במצלמה או במיקרופון."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"מכשיר"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"הסוללה טעונה"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"בטעינה"</string>
@@ -1191,4 +1191,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את האריח הבא"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"הוספת אריח"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"אין להוסיף אריח"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7e8c6a1..8f07a2c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"マイクの使用が許可されているすべてのアプリとサービスでアクセスのブロックが解除されます。"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"カメラの使用が許可されているすべてのアプリとサービスでアクセスのブロックが解除されます。"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"カメラやマイクの使用が許可されているすべてのアプリとサービスでアクセスのブロックが解除されます。"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"デバイス"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"充電が完了しました"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電しています"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> が以下のタイルをクイック設定に追加しようとしています"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"タイルを追加"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"タイルを追加しない"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index f7bcf7f..c6e2bec 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ამ მოქმედების მეშვეობით განიბლოკება ყველა აპსა და მომსახურებაზე წვდომა, რომელთაც აქვთ თქვენი მიკროფონის გამოყენების უფლება."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ამ მოქმედების მეშვეობით განიბლოკება ყველა აპსა და მომსახურებაზე წვდომა, რომელთაც აქვთ თქვენი კამერის გამოყენების უფლება."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ამ მოქმედების მეშვეობით განიბლოკება ყველა აპსა და მომსახურებაზე წვდომა, რომელთაც აქვთ თქვენი კამერის ან მიკროფონის გამოყენების უფლება."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"მოწყობილობა"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"დატენილია"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"მიმდინარეობს დატენვა"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>-ს სურს, დაამატოს შემდეგი მოზაიკა სწრაფ პარამეტრებში"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"მოზაიკის დამატება"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"არ დაემატოს მოზაიკა"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 92b93a0..4fb68e9 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Микрофоныңызды пайдалануға рұқсат берілген барлық қолданба мен қызметтің бөгеуі алынады."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Камераңызды пайдалануға рұқсат берілген барлық қолданба мен қызметтің бөгеуі алынады."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Камераңызды немесе микрофоныңызды пайдалануға рұқсат берілген барлық қолданба мен қызметтің бөгеуі алынады."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Құрылғы"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Зарядталды"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарядталуда"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> Жылдам параметрлерге келесі бөлшекті қосқысы келеді."</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Бөлшек қосу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Бөлшек қоспау"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 2b25be6..5e734f6 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ការធ្វើបែបនេះនឹងឈប់ទប់ស្កាត់ការចូលប្រើសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់ ដែលត្រូវបានអនុញ្ញាតឱ្យប្រើមីក្រូហ្វូនរបស់អ្នក។"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ការធ្វើបែបនេះនឹងឈប់ទប់ស្កាត់ការចូលប្រើសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់ ដែលត្រូវបានអនុញ្ញាតឱ្យប្រើកាមេរ៉ារបស់អ្នក។"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ការធ្វើបែបនេះនឹងឈប់ទប់ស្កាត់ការចូលប្រើសម្រាប់កម្មវិធី និងសេវាកម្មទាំងអស់ ដែលត្រូវបានអនុញ្ញាតឱ្យប្រើកាមេរ៉ា ឬមីក្រូហ្វូនរបស់អ្នក។"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ឧបករណ៍"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើកទិដ្ឋភាពរួម"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"បានសាកថ្មពេញ"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"កំពុងសាកថ្ម"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូលប្រអប់"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"កុំបញ្ចូលប្រអប់"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 13b4bd7..798ea99 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ಇದು ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳಿಗೆ ಹಾಗೂ ಸೇವೆಗಳಿಗೆ ನಿಮ್ಮ ಮೈಕ್ರೋಫೋನ್ ಬಳಸುವುದಕ್ಕಾಗಿ ಇರುವ ಪ್ರವೇಶದ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ಇದು ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳಿಗೆ ಹಾಗೂ ಸೇವೆಗಳಿಗೆ ನಿಮ್ಮ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುವುದಕ್ಕಾಗಿ ಇರುವ ಪ್ರವೇಶದ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ಇದು ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳಿಗೆ ಹಾಗೂ ಸೇವೆಗಳಿಗೆ ನಿಮ್ಮ ಕ್ಯಾಮರಾ ಅಥವಾ ಮೈಕ್ರೋಫೋನ್ ಬಳಸುವುದಕ್ಕಾಗಿ ಇರುವ ಪ್ರವೇಶದ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ಸಾಧನ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</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">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"ಮೊಬೈಲ್ ಡೇಟಾ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್ವರ್ಕ್ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"ಯಾವುದೇ ನೆಟ್ವರ್ಕ್ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ಈ ಕೆಳಗಿನ ಟೈಲ್ ಅನ್ನು ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಸೇರಿಸಲು ಬಯಸುತ್ತದೆ"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಬೇಡಿ"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 81b0ec0..de38570 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"마이크를 사용할 수 있는 모든 앱 및 서비스에 대해 액세스가 차단 해제됩니다."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"카메라를 사용할 수 있는 모든 앱 및 서비스에 대해 액세스가 차단 해제됩니다."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"카메라 또는 마이크를 사용할 수 있는 모든 앱 및 서비스에 대해 액세스가 차단 해제됩니다."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"기기"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"충전됨"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"충전 중"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>에서 빠른 설정에 다음 타일을 추가하려고 합니다."</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"타일 추가"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"타일 추가 안함"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 3161270..90f4689 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Микрофонуңузду колдонууга уруксат алган бардык колдонмолор менен кызматтар бөгөттөн чыгат."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Камераны колдонууга уруксат алган бардык колдонмолор менен кызматтар бөгөттөн чыгат."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Камераңызды же микрофонуңузду колдонууга уруксат алган бардык колдонмолор менен кызматтар бөгөттөн чыгат."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Түзмөк"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Кубатталды"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Кубатталууда"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> төмөнкү ыкчам баскычты Ыкчам жөндөөлөргө кошкону жатат"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ыкчам баскыч кошуу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ыкчам баскыч кошулбасын"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 509a6f8..7f20d1f 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ນີ້ຈະຍົກເລີກການບລັອກການເຂົ້າເຖິງແອັບ ແລະ ບໍລິການທັງໝົດທີ່ອະນຸຍາດໃຫ້ໃຊ້ໄມໂຄຣໂຟນຂອງທ່ານ."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ນີ້ຈະຍົກເລີກການບລັອກການເຂົ້າເຖິງແອັບ ແລະ ບໍລິການທັງໝົດທີ່ອະນຸຍາດໃຫ້ໃຊ້ກ້ອງຖ່າຍຮູບຂອງທ່ານ."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ນີ້ຈະປົດບລັອກການເຂົ້າເຖິງແອັບ ແລະ ບໍລິການທັງໝົດທີ່ອະນຸຍາດໃຫ້ໃຊ້ກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນຂອງທ່ານ."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ອຸປະກອນ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ສາກເຕັມແລ້ວ."</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ກຳລັງສາກໄຟ"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ຕ້ອງການເພີ່ມແຜ່ນຕໍ່ໄປນີ້ໃສ່ການຕັ້ງຄ່າດ່ວນ"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ເພີ່ມແຜ່ນ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ຢ່າເພີ່ມແຜ່ນ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 87d67b7..03858d5 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tai atlikus visų programų ir paslaugų prieigos blokavimas panaikinamas ir joms leidžiama naudoti mikrofoną."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tai atlikus visų programų ir paslaugų prieigos blokavimas panaikinamas ir joms leidžiama naudoti fotoaparatą."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tai atlikus visų programų ir paslaugų prieigos blokavimas panaikinamas ir joms leidžiama naudoti fotoaparatą ar mikrofoną."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Įrenginys"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Įkrautas"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Kraunamas"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ nori prie sparčiųjų nustatymų pridėti toliau pateiktą išklotinės elementą"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridėti išklotinės elementą"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridėti išklotinės elemento"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 242d6ba..2db4fd4 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Visas lietotnes un pakalpojumi, kurām ir atļauts izmantot mikrofonu, varēs tam piekļūt."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Visas lietotnes un pakalpojumi, kuriem ir atļauts izmantot kameru, varēs tai piekļūt."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Visas lietotnes un pakalpojumi, kuriem ir atļauts izmantot kameru vai mikrofonu, varēs tiem piekļūt."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Ierīce"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Akumulators uzlādēts"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Notiek uzlāde"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> pieprasa atļauju pievienot tālāk norādīto elementu ātrajiem iestatījumiem"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pievienot elementu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepievienot elementu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index fe4040e..b1a5a5e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ова ќе го одблокира пристапот за сите апликации и услуги на кои им е дозволено користење на микрофонот."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ова ќе го одблокира пристапот за сите апликации и услуги на кои им е дозволено користење на камерата."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ова ќе го одблокира пристапот за сите апликации и услуги на кои им е дозволено користење на камерата или микрофонот."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Уред"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Наполнета"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Се полни"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> сака да ја додаде следнава плочка на „Брзите поставки“"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додајте плочка"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавајте плочка"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index db170de..dd937a8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"നിങ്ങളുടെ മൈക്രോഫോൺ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്ന എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കുമുള്ള ആക്സസ് ഇത് അൺബ്ലോക്ക് ചെയ്യുന്നു."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"നിങ്ങളുടെ ക്യാമറ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്ന എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കുമുള്ള ആക്സസ് ഇത് അൺബ്ലോക്ക് ചെയ്യുന്നു."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"നിങ്ങളുടെ ക്യാമറയോ മൈക്രോഫോണോ ഉപയോഗിക്കാൻ അനുവദിച്ചിരിക്കുന്ന എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കുമുള്ള ആക്സസ് ഇത് അൺബ്ലോക്ക് ചെയ്യുന്നു."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ഉപകരണം"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ചാർജായി"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ചാർജ് ചെയ്യുന്നു"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ദ്രുത ക്രമീകരണത്തിലേക്ക് ഇനിപ്പറയുന്ന ടൈൽ ചേർക്കാൻ <xliff:g id="APPNAME">%1$s</xliff:g> ആവശ്യപ്പെടുന്നു"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ടൈൽ ചേർക്കുക"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ടൈൽ ചേർക്കരുത്"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index aed89bb..341b22d 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Энэ нь таны микрофоныг ашиглах зөвшөөрөлтэй бүх апп болон үйлчилгээний хандалтыг блокоос гаргана."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Энэ нь таны камерыг ашиглах зөвшөөрөлтэй бүх апп болон үйлчилгээний хандалтыг блокоос гаргана."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Энэ нь таны камер эсвэл микрофоныг ашиглах зөвшөөрөлтэй бүх апп болон үйлчилгээний хандалтыг блокоос гаргана."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Төхөөрөмж"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Цэнэглэгдсэн"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Цэнэглэж байна"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүсэлтэй байна"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Хавтан нэмэх"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Хавтанг бүү нэм"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 633519a..c071582 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -248,7 +248,7 @@
<skip />
<string name="accessibility_notification_dismissed" msgid="4411652015138892952">"सूचना डिसमिस केल्या."</string>
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"सूचना शेड."</string>
- <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"द्रुत सेटिंग्ज."</string>
+ <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"क्विक सेटिंग्ज."</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"लॉक स्क्रीन."</string>
<string name="accessibility_desc_settings" msgid="6728577365389151969">"सेटिंग्ज"</string>
<string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"अवलोकन."</string>
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"हे तुमचा मायक्रोफोन वापरण्याची परवानगी असलेल्या सर्व ॲप्स आणि सेवांसाठी अॅक्सेस अनब्लॉक करते."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"हे तुमचा कॅमेरा वापरण्याची परवानगी असलेल्या सर्व ॲप्स आणि सेवांसाठी अॅक्सेस अनब्लॉक करते."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"हे तुमचा कॅमेरा आणि मायक्रोफोन वापरण्याची परवानगी असलेल्या सर्व ॲप्स व सेवांसाठी अॅक्सेस अनब्लॉक करते."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"डिव्हाइस"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"चार्ज झाली"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज होत आहे"</string>
@@ -659,7 +659,7 @@
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम UI ट्युनर"</string>
<string name="show_battery_percentage" msgid="6235377891802910455">"एम्बेडेड बॅटरी टक्केवारी दर्शवा"</string>
<string name="show_battery_percentage_summary" msgid="9053024758304102915">"चार्ज होत नसताना स्टेटस बार चिन्हामध्ये बॅटरी पातळी टक्केवारी दर्शवा"</string>
- <string name="quick_settings" msgid="6211774484997470203">"द्रुत सेटिंग्ज"</string>
+ <string name="quick_settings" msgid="6211774484997470203">"क्विक सेटिंग्ज"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
<string name="overview" msgid="3522318590458536816">"अवलोकन"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम UI डेमो मोड"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</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">"कनेक्ट केले आहे"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"मोबाइल डेटा ऑटो-कनेक्ट होणार नाही"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"कोणतेही नेटवर्क उपलब्ध नाही"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ला क्विक सेटिंग्जमध्ये पुढील टाइल जोडायची आहे"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोडा"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल जोडू नका"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index a689bb3..8388cdd 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan mikrofon anda."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera anda."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera atau mikrofon anda."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Peranti"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Sudah dicas"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Mengecas"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin yang berikut kepada Tetapan Pantas"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan jubin"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah jubin"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 4975a0a..a40f944 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"၎င်းက သင့်မိုက်ခရိုဖုန်းသုံးရန် ခွင့်ပြုထားသော အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် သုံးခွင့်ကို ပြန်ဖွင့်ပေးသည်။"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"၎င်းက သင့်ကင်မရာသုံးရန် ခွင့်ပြုထားသော အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် သုံးခွင့်ကို ပြန်ဖွင့်ပေးသည်။"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"၎င်းက သင့်ကင်မရာ (သို့) မိုက်ခရိုဖုန်းသုံးရန် ခွင့်ပြုထားသော အက်ပ်နှင့် ဝန်ဆောင်မှုအားလုံးအတွက် သုံးခွင့်ကို ပြန်ဖွင့်ပေးသည်။"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"စက်"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"အားသွင်းပြီး"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"အားသွင်းနေ"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> က ‘အမြန် ဆက်တင်များ’ တွင် အောက်ပါအကွက်ငယ်ကို ထည့်လိုသည်"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"အကွက်ငယ် ထည့်ရန်"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"အကွက်ငယ် မထည့်ပါ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index af8d47b..5542ad3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Dette opphever blokkeringen av tilgang for alle apper og tjenester som har tillatelse til å bruke mikrofonen."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Dette opphever blokkeringen av tilgang for alle apper og tjenester som har tillatelse til å bruke kameraet."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Dette opphever blokkeringen av tilgang for alle apper og tjenester som har tillatelse til å bruke kameraet eller mikrofonen."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Oppladet"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Lader"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil legge til denne brikken i Hurtiginnstillinger"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Legg til brikke"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ikke legg til brikke"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b6a186e..9a2e9a1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"यन्त्र"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"चार्ज भयो"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हुँदै छ"</string>
@@ -1179,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> द्रुत सेटिङमा निम्न टाइल हाल्न चाहन्छ"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल हाल्नुहोस्"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल नहाल्नुहोस्"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 461505f..ffcc3a8 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -48,4 +48,16 @@
<item name="android:windowLightStatusBar">false</item>
</style>
+ <style name="TextAppearance.InternetDialog.Active">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/connected_network_primary_color</item>
+ <item name="android:textDirection">locale</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Secondary.Active">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/connected_network_secondary_color</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 135ca57..8851c70e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Hiermee hef je de toegangsblokkering op voor alle apps en services die rechten hebben om je microfoon te gebruiken."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Hiermee hef je de toegangsblokkering op voor alle apps en services die rechten hebben om je camera te gebruiken."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Hiermee hef je de toegangsblokkering op voor alle apps en services die rechten hebben om je camera of microfoon te gebruiken."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Apparaat"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Opgeladen"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Opladen"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil de volgende tegel toevoegen aan Snelle instellingen"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tegel toevoegen"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tegel niet toevoegen"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1d5ff4b..3b2f79d 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ଆପଣଙ୍କ ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇଥିବା ସମସ୍ତ ଆପ୍ ଓ ସେବା ପାଇଁ ଏହା ଆକ୍ସେସକୁ ଅନବ୍ଲକ୍ କରେ।"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ଆପଣଙ୍କ କ୍ୟାମେରାକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇଥିବା ସମସ୍ତ ଆପ୍ ଓ ସେବା ପାଇଁ ଏହା ଆକ୍ସେସକୁ ଅନବ୍ଲକ୍ କରେ।"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ଆପଣଙ୍କ କ୍ୟାମେରା କିମ୍ବା ମାଇକ୍ରୋଫୋନକୁ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନୁମତି ଦିଆଯାଇଥିବା ସମସ୍ତ ଆପ୍ ଓ ସେବା ପାଇଁ ଏହା ଆକ୍ସେସକୁ ଅନବ୍ଲକ୍ କରେ।"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ଡିଭାଇସ୍"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ଚାର୍ଜ ହୋଇଗଲା"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ଚାର୍ଜ କରାଯାଉଛି"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</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">"ସଂଯୋଗ କରାଯାଇଛି"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> କ୍ୱିକ୍ ସେଟିଂସରେ ନିମ୍ନୋକ୍ତ ଟାଇଲ୍ ଯୋଗ କରିବାକୁ ଚାହେଁ"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ଟାଇଲ୍ ଯୋଗ କର ନାହିଁ"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 055d75b..88d158d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ਇਹ ਉਹਨਾਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਪਹੁੰਚ ਨੂੰ ਅਣਬਲਾਕ ਕਰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਹਾਡਾ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ਇਹ ਉਹਨਾਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਪਹੁੰਚ ਨੂੰ ਅਣਬਲਾਕ ਕਰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਹਾਡਾ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"ਇਹ ਉਹਨਾਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ ਪਹੁੰਚ ਨੂੰ ਅਣਬਲਾਕ ਕਰਦਾ ਹੈ ਜਿਨ੍ਹਾਂ ਨੂੰ ਤੁਹਾਡਾ ਕੈਮਰਾ ਜਾਂ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"ਡੀਵਾਈਸ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ਚਾਰਜ ਹੋਇਆ"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ਚਾਰਜ ਕਰ ਰਿਹਾ ਹੈ"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</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">"ਕਨੈਕਟ ਹੈ"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"ਮੋਬਾਈਲ ਡਾਟਾ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"ਕੋਈ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੀ ਟਾਇਲ ਨੂੰ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ਟਾਇਲ ਸ਼ਾਮਲ ਨਾ ਕਰੋ"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index fc32959..969b38b 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Spowoduje to odblokowanie dostępu dla wszystkich aplikacji i usług, które mają uprawnienia do korzystania z mikrofonu."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Spowoduje to odblokowanie dostępu dla wszystkich aplikacji i usług, które mają uprawnienia do korzystania z aparatu."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Spowoduje to odblokowanie dostępu dla wszystkich aplikacji i usług, które mają uprawnienia do korzystania z aparatu lub mikrofonu."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Urządzenie"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Naładowana"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Ładowanie"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> chce dodać do Szybkich ustawień ten kafelek"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj kafelek"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nie dodawaj kafelka"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 24880122..9546571 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar seu microfone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera ou seu microfone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregado"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Carregando"</string>
@@ -597,7 +597,7 @@
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
- <string name="accessibility_volume_expand" msgid="7653070939304433603">"Expandir"</string>
+ <string name="accessibility_volume_expand" msgid="7653070939304433603">"Abrir"</string>
<string name="accessibility_volume_collapse" msgid="2746845391013829996">"Recolher"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Dica de legenda"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 68d306d..173eaa5 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Isto desbloqueia o acesso a todas as apps e serviços com autorização para utilizar o seu microfone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Isto desbloqueia o acesso a todas as apps e serviços com autorização para utilizar a sua câmara."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Isto desbloqueia o acesso a todas as apps e serviços com autorização para utilizar a sua câmara ou microfone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"A carregar"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A app <xliff:g id="APPNAME">%1$s</xliff:g> pretende adicionar o seguinte mosaico às Definições rápidas"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar mosaico"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicion. mosaico"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 24880122..9546571 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar seu microfone."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Essa ação desbloqueia o acesso para todos os apps e serviços com autorização para usar sua câmera ou seu microfone."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispositivo"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Carregado"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Carregando"</string>
@@ -597,7 +597,7 @@
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="5901885672973736563">"Desativar agora"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
- <string name="accessibility_volume_expand" msgid="7653070939304433603">"Expandir"</string>
+ <string name="accessibility_volume_expand" msgid="7653070939304433603">"Abrir"</string>
<string name="accessibility_volume_collapse" msgid="2746845391013829996">"Recolher"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Dica de legenda"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 169108a..3bca61f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi microfonul."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Astfel, deblocați accesul pentru toate aplicațiile și serviciile care au permisiunea de a folosi camera sau microfonul."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Dispozitiv"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comutați secțiunea Recente"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Încărcată"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Se încarcă"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vrea să adauge următorul card la Setări rapide"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adăugați un card"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 1859acd..84d4210 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование микрофона."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры или микрофона."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Устройство"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Батарея заряжена"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Зарядка батареи"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" хочет добавить в меню \"Быстрые настройки\" указанный параметр."</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавить параметр"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не добавлять"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2df39d7..65855f4 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"මෙය ඔබගේ මයික්රෆෝනය භාවිත කිරීමට ඉඩ දී ඇති සියලු යෙදුම් සහ සේවා සඳහා ප්රවේශය අවහිර කිරීම ඉවත් කරයි."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"මෙය ඔබගේ කැමරාව භාවිතා කිරීමට ඉඩ දී ඇති සියලු යෙදුම් සහ සේවා සඳහා ප්රවේශය අවහිර කිරීම ඉවත් කරයි."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"මෙය ඔබගේ කැමරාව හෝ මයික්රෆෝනය භාවිත කිරීමට ඉඩ දී ඇති සියලු යෙදුම් සහ සේවා සඳහා ප්රවේශය අවහිර කිරීම ඉවත් කරයි."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"උපාංගය"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"අරෝපිතයි"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ආරෝපණය වෙමින්"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> හට ක්ෂණික සැකසීම් වෙත පහත ටයිල් එක් කිරීමට අවශ්යයි"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ටයිල් එක් කරන්න"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ටයිල් එක් නොකරන්න"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6ef5ad5..1202041 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Táto akcia odblokuje prístup všetkým aplikáciám a službám, ktoré majú povolené používať mikrofón."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Táto akcia odblokuje prístup všetkým aplikáciám a službám, ktoré majú povolené používať fotoaparát."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Táto akcia odblokuje prístup všetkým aplikáciám a službám, ktoré majú povolené používať fotoaparát alebo mikrofón."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Zariadenie"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Nabitá"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nabíja sa"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> chce pridať do rýchlych nastavení túto kartu"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridať kartu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridať kartu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 977104c..94437330 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"S tem boste odblokirali dostop za vse aplikacije in storitve, ki imajo dovoljenje za uporabo mikrofona."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"S tem boste odblokirali dostop za vse aplikacije in storitve, ki imajo dovoljenje za uporabo fotoaparata."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"S tem boste odblokirali dostop za vse aplikacije in storitve, ki imajo dovoljenje za uporabo fotoaparata ali mikrofona."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Naprava"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Baterija napolnjena"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Polnjenje"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> želi dodati to ploščico v hitre nastavitve."</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj ploščico"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj ploščice"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 970b84c..b6789a8 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Kjo zhbllokon qasjen për të gjitha aplikacionet dhe shërbimet që lejohen të përdorin mikrofonin tënd."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kjo zhbllokon qasjen për të gjitha aplikacionet dhe shërbimet që lejohen të përdorin kamerën tënde."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kjo zhbllokon qasjen për të gjitha aplikacionet dhe shërbimet që lejohen të përdorin kamerën ose mikrofonin tënd."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Pajisja"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"I karikuar"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Po karikohet"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</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">"Lidhur"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Të dhënat celulare nuk do të lidhen automatikisht"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Nuk ofrohet asnjë rrjet"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> dëshiron të shtojë pllakëzën e mëposhtme te \"Cilësimet e shpejta\""</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Shto një pllakëz"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mos e shto pllakëzën"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e83b423..e4e324b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -437,7 +437,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Овим ће се одблокирати приступ за све апликације и услуге које имају дозволу за коришћење микрофона."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Овим ће се одблокирати приступ за све апликације и услуге које имају дозволу за коришћење камере."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Овим ће се одблокирати приступ за све апликације и услуге које имају дозволу за коришћење камере или микрофона."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Уређај"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Напуњена је"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Пуни се"</string>
@@ -1185,4 +1185,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> жели да дода следећу плочицу у Брза подешавања"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додај плочицу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додај плочицу"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index bd819e5..b3dc3fc 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Detta återaktiverar åtkomsten för alla appar och tjänster som tillåts att använda mikrofonen."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Detta återaktiverar åtkomsten för alla appar och tjänster som tillåts att använda kameran."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Detta återaktiverar åtkomsten för alla appar och tjänster som tillåts att använda kameran eller mikrofonen."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Enhet"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Laddat"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Laddar"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill lägga till följande ruta i snabbinställningarna"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lägg till ruta"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Lägg inte till ruta"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 121bcd2..fbf3094 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Hatua hii huruhusu programu na huduma zote zenye idhini zitumie maikrofoni yako."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Hatua hii huruhusu programu na huduma zote zenye idhini zitumie kamera yako."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Hatua hii huruhusu programu na huduma zote zenye idhini zitumie kamera au maikrofoni yako."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Kifaa"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Betri imejaa"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Inachaji"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingependa kuongeza kigae kifuatacho kwenye Mipangilio ya Haraka"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ongeza kigae"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Usiongeze kigae"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 74bdd32..18f94ac 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"உங்கள் மைக்ரோஃபோனைப் பயன்படுத்த அனுமதிக்கப்பட்டுள்ள அனைத்து ஆப்ஸ் மற்றும் சேவைகளை அணுகுவதற்கான தடுப்பை இது நீக்கும்."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"உங்கள் கேமராவைப் பயன்படுத்த அனுமதிக்கப்பட்டுள்ள அனைத்து ஆப்ஸ் மற்றும் சேவைகளை அணுகுவதற்கான தடுப்பை இது நீக்கும்."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"உங்கள் கேமராவையோ மைக்ரோஃபோனையோ பயன்படுத்த அனுமதிக்கப்பட்டுள்ள அனைத்து ஆப்ஸ் மற்றும் சேவைகளை அணுகுவதற்கான தடுப்பை இது நீக்கும்."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"சாதனம்"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"சார்ஜ் செய்யப்பட்டது"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"சார்ஜ் ஆகிறது"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"விரைவு அமைப்புகளில் பின்வரும் கட்டத்தைச் சேர்க்க <xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸ் விரும்புகிறது"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"கட்டத்தைச் சேர்"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"கட்டத்தை சேர்க்காதே"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b643685..ec279d0 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"మీ మైక్రోఫోన్ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"మీ కెమెరాను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"మీ కెమెరాను లేదా మైక్రోఫోన్ను ఉపయోగించడానికి అనుమతి పొందిన అన్ని యాప్లు, సర్వీస్లకు యాక్సెస్ను ఇది అన్బ్లాక్ చేస్తుంది."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"పరికరం"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ఛార్జ్ చేయబడింది"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"ఛార్జ్ అవుతోంది"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</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">"కనెక్ట్ చేయబడింది"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"మొబైల్ డేటా ఆటోమెటిక్గా కనెక్ట్ అవ్వదు"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్వర్క్లేవీ అందుబాటులో లేవు"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"నెట్వర్క్లు అందుబాటులో లేవు"</string>
@@ -1180,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"కింది టైల్ను క్విక్ సెట్టింగ్లకు జోడించడానికి <xliff:g id="APPNAME">%1$s</xliff:g> అనుమతి కోరుతోంది"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"టైల్ను జోడించండి"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"టైల్ను జోడించవద్దు"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్ను ఎంచుకోండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index b27cd3e..639432d 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"การดำเนินการนี้จะเลิกบล็อกสิทธิ์เข้าถึงของแอปและบริการทั้งหมดที่ได้รับอนุญาตให้ใช้ไมโครโฟนของคุณ"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"การดำเนินการนี้จะเลิกบล็อกสิทธิ์เข้าถึงของแอปและบริการทั้งหมดที่ได้รับอนุญาตให้ใช้กล้องของคุณ"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"การดำเนินการนี้จะเลิกบล็อกสิทธิ์เข้าถึงของแอปและบริการทั้งหมดที่ได้รับอนุญาตให้ใช้กล้องหรือไมโครโฟนของคุณ"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"อุปกรณ์"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"ชาร์จแล้ว"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"กำลังชาร์จ"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ต้องการเพิ่มชิ้นส่วนต่อไปนี้ในการตั้งค่าด่วน"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"เพิ่มชิ้นส่วน"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ไม่ต้องเพิ่มชิ้นส่วน"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 11a23f8..4da06b8 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Ina-unblock nito ang access para sa lahat ng app at serbisyong pinapayagang gumamit ng iyong mikropono."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Ina-unblock nito ang access para sa lahat ng app at serbisyong pinapayagang gumamit ng iyong camera."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Ina-unblock nito ang access para sa lahat ng app at serbisyong pinapayagang gumamit ng iyong camera o mikropono."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Device"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Tapos nang mag-charge"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Nagcha-charge"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Gustong idagdag ng <xliff:g id="APPNAME">%1$s</xliff:g> ang sumusunod na tile sa Mga Mabilisang Setting"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Idagdag ang tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Huwag idagdag"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index a5c06f1..88665e0 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Bu işlem, mikrofonunuzu kullanmasına izin verilen tüm uygulama ve hizmetlere erişimin engellemesini kaldırır."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Bu işlem, kameranızı kullanmasına izin verilen tüm uygulama ve hizmetlere erişimin engellemesini kaldırır."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Bu işlem, kamera veya mikrofonunuzu kullanmasına izin verilen tüm uygulama ve hizmetlere erişimin engellemesini kaldırır."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Cihaz"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Şarj oldu"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Şarj oluyor"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdaki kartı Hızlı Ayarlar\'a eklemek istiyor"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kart ekle"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kart ekleme"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 616b0f2..267fe09 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -439,7 +439,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Усі додатки та сервіси, яким дозволено користуватися вашим мікрофоном, отримають доступ."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Усі додатки та сервіси, яким дозволено користуватися вашою камерою, отримають доступ."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Усі додатки та сервіси, яким дозволено користуватися вашою камерою чи мікрофоном, отримають доступ."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Пристрій"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Заряджено"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Заряджається"</string>
@@ -1191,4 +1191,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Додаток <xliff:g id="APPNAME">%1$s</xliff:g> хоче додати такий параметр у меню швидких налаштувань:"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додати параметр"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавати параметр"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index a42729e..f8e5ca1 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"اس سے آپ کا مائیکروفون استعمال کرنے کے لیے اجازت یافتہ سبھی ایپس اور سروسز کے لیے رسائی غیر مسدود ہو جاتی ہے۔"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"اس سے آپ کا کیمرا استعمال کرنے کے لیے اجازت یافتہ سبھی ایپس اور سروسز کے لیے رسائی غیر مسدود ہو جاتی ہے۔"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"اس سے آپ کا کیمرا یا مائیکروفون استعمال کرنے کے لیے اجازت یافتہ سبھی ایپس اور سروسز کے لیے رسائی غیر مسدود ہو جاتی ہے۔"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"آلہ"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"چارج ہوگئی"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"چارج ہو رہی ہے"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
<string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
<string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"موبائل ڈیٹا خودکار طور پر منسلک نہیں ہوگا"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"کوئی نیٹ ورکس دستیاب نہیں ہیں"</string>
@@ -1180,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتی ہے"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ٹائل شامل کریں"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ٹائل شامل نہ کریں"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index e91ec32..b3a605c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Mikrofoningizdan foydalanishga ruxsat berilgan barcha ilovalar va xizmatlar uchun ruxsatni blokdan chiqaradi."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Kamerangizdan foydalanishga ruxsat berilgan barcha ilovalar va xizmatlar uchun ruxsatni blokdan chiqaradi."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Kamera va mikrofoningizdan foydalanishga ruxsat berilgan barcha ilovalar va xizmatlar uchun ruxsatni blokdan chiqaradi."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Qurilma"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Quvvat oldi"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Quvvat olmoqda"</string>
@@ -1165,8 +1165,7 @@
<string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</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">"Ulandi"</string>
- <!-- no translation found for mobile_data_off_summary (3663995422004150567) -->
- <skip />
+ <string name="mobile_data_off_summary" msgid="3663995422004150567">"Mobil internetga avtomatik ulanmaydi"</string>
<string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
<string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
<string name="all_network_unavailable" msgid="4112774339909373349">"Hech qanday tarmoq mavjud emas"</string>
@@ -1180,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ilovasi Tezkor sozlamalarga quyidagi tugmani kiritmoqchi"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tugma kiritish"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tugma kiritilmasin"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index a52c999..e952e83 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Thao tác này sẽ bỏ chặn quyền truy cập cho mọi ứng dụng và dịch vụ được phép sử dụng micrô của bạn."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Thao tác này sẽ bỏ chặn quyền truy cập cho mọi ứng dụng và dịch vụ được phép sử dụng máy ảnh của bạn."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Thao tác này sẽ bỏ chặn quyền truy cập cho mọi ứng dụng và dịch vụ được phép sử dụng máy ảnh hoặc micrô của bạn."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Thiết bị"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Đã sạc đầy"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Đang sạc"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> muốn thêm ô bên dưới vào trình đơn Cài đặt nhanh"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Thêm ô"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Không thêm ô"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d9fadf6..eab642c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"这将会为所有获准使用您麦克风的应用和服务启用这项权限。"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"这将会为所有获准使用您摄像头的应用和服务启用这项权限。"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"这将会为所有获准使用您的摄像头或麦克风的应用和服务启用这项权限。"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"设备"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"已充满"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"正在充电"</string>
@@ -1179,4 +1179,6 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”希望将以下图块添加到“快捷设置”"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加图块"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加图块"</string>
+ <!-- no translation found for qs_user_switch_dialog_title (3045189293587781366) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index ee853ce..b25851b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"解除封鎖後,凡有存取權的應用程式和服務都可使用您的麥克風。"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"解除封鎖後,凡有存取權的應用程式和服務都可使用您的相機。"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"解除封鎖後,凡有存取權的應用程式和服務都可使用您的相機或麥克風。"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"已完成充電"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電中"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在「快速設定」選單新增以下圖塊"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增圖塊"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增圖塊"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index dccbcc4..4ef03a6 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"這麼做可允許所有應用程式和服務使用麥克風。"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"這麼做可允許所有應用程式和服務使用相機。"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"這麼做可允許所有應用程式和服務使用相機或麥克風。"</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"裝置"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"已充飽"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"充電中"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在快速設定選單新增以下設定方塊"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增設定方塊"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增設定方塊"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 683890f..30f0f4e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -435,7 +435,7 @@
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Lokhu kuvulela ukufinyelela kwawo wonke ama-app namasevisi avunyelwe ukusebenzisa imakrofoni yakho."</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Lokhu kuvulela ukufinyelela kwawo wonke ama-app namasevisi avunyelwe ukusebenzisa ikhamera yakho."</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Lokhu kuvulela ukufinyelela kwawo wonke ama-app namasevisi avunyelwe ukusebenzisa ikhamera yakho noma imakrofoni."</string>
- <string name="media_seamless_remote_device" msgid="177033467332920464">"Idivayisi"</string>
+ <string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"Kushajiwe"</string>
<string name="expanded_header_battery_charging" msgid="1717522253171025549">"Iyashaja"</string>
@@ -1179,4 +1179,5 @@
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"I-<xliff:g id="APPNAME">%1$s</xliff:g> ifuna ukwengeza ithayela elilandelayo Kumasethingi Asheshayo"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engeza ithayela"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ungafaki ithayela"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2c19212..08778bf 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -192,9 +192,6 @@
<color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 -->
<color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 -->
- <!-- Logout button -->
- <color name="logout_button_bg_color">#ccffffff</color>
-
<!-- Color for the Assistant invocation lights -->
<color name="default_invocation_lights_color">#ffffffff</color> <!-- white -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fb90d63..483dfc6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -197,11 +197,6 @@
low powered state yet. -->
<bool name="doze_long_press_uses_prox">true</bool>
- <!-- Doze: whether the brightness sensor uses the proximity sensor.
- If both this parameter and doze_selectively_register_prox are true, registration for the
- brightness sensor won't occur when the display state is ON. -->
- <bool name="doze_brightness_uses_prox">true</bool>
-
<!-- Doze: should notifications be used as a pulse signal? -->
<bool name="doze_pulse_on_notifications">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6d792be..45ea488 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1250,10 +1250,7 @@
<integer name="wired_charging_keyguard_text_animation_distance">-30</integer>
<!-- Logout button -->
- <dimen name="logout_button_layout_height">32dp</dimen>
- <dimen name="logout_button_padding_horizontal">16dp</dimen>
- <dimen name="logout_button_margin_bottom">12dp</dimen>
- <dimen name="logout_button_corner_radius">4dp</dimen>
+ <dimen name="logout_button_corner_radius">50dp</dimen>
<!-- Blur radius on status bar window and power menu -->
<dimen name="min_window_blur_radius">1px</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 5ef9163..6729f3d 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -22,7 +22,7 @@
<bool name="flag_notification_pipeline2_rendering">false</bool>
<bool name="flag_notif_updates">true</bool>
- <bool name="flag_monet">false</bool>
+ <bool name="flag_monet">true</bool>
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">true</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8f80421..49bbf7d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3022,6 +3022,8 @@
<string name="wifi_failed_connect_message">Failed to connect to network</string>
<!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
<string name="see_all_networks">See all</string>
+ <!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
+ <string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
<!-- Text for TileService request dialog. This is shown to the user that an app is requesting
user approval to add the shown tile to Quick Settings [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index dc17afb..33edca1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -994,4 +994,60 @@
<item name="android:maxHeight">4dp</item>
</style>
+ <!-- Internet Dialog -->
+ <style name="InternetDialog">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center_vertical|start</item>
+ <item name="android:layout_marginStart">@dimen/internet_dialog_network_layout_margin</item>
+ </style>
+
+ <style name="InternetDialog.Network">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">88dp</item>
+ <item name="android:layout_marginEnd">@dimen/internet_dialog_network_layout_margin</item>
+ <item name="android:paddingStart">22dp</item>
+ <item name="android:paddingEnd">22dp</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:focusable">true</item>
+ <item name="android:clickable">true</item>
+ </style>
+
+ <style name="InternetDialog.NetworkTitle">
+ <item name="android:layout_marginEnd">7dp</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog</item>
+ </style>
+
+ <style name="InternetDialog.NetworkTitle.Active">
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
+ </style>
+
+ <style name="InternetDialog.NetworkSummary">
+ <item name="android:layout_marginEnd">34dp</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary</item>
+ </style>
+
+ <style name="InternetDialog.NetworkSummary.Active">
+ <item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
+ </item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textDirection">locale</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Secondary">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
+ </style>
+
+ <style name="TextAppearance.InternetDialog.Active"/>
+
+ <style name="TextAppearance.InternetDialog.Secondary.Active"/>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
index e5933e6..9010d51 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -36,6 +36,13 @@
* [View.setTranslationY]
*/
private val translationApplier: TranslationApplier = object : TranslationApplier {},
+ /**
+ * Allows to set custom implementation for getting
+ * view location. Could be useful if logical view bounds
+ * are different than actual bounds (e.g. view container may
+ * have larger width than width of the items in the container)
+ */
+ private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {}
) : UnfoldTransitionProgressProvider.TransitionProgressListener {
private val screenSize = Point()
@@ -43,6 +50,8 @@
private val animatedViews: MutableList<AnimatedView> = arrayListOf()
+ private var lastAnimationProgress: Float = 0f
+
/**
* Updates display properties in order to calculate the initial position for the views
* Must be called before [registerViewForAnimation]
@@ -58,6 +67,19 @@
}
/**
+ * If target view positions have changed (e.g. because of layout changes) call this method
+ * to re-query view positions and update the translations
+ */
+ fun updateViewPositions() {
+ animatedViews.forEach { animatedView ->
+ animatedView.view.get()?.let {
+ animatedView.updateAnimatedView(it)
+ }
+ }
+ onTransitionProgress(lastAnimationProgress)
+ }
+
+ /**
* Registers a view to be animated, the view should be measured and layouted
* After finishing the animation it is necessary to clear
* the views using [clearRegisteredViews]
@@ -85,45 +107,30 @@
)
}
}
+ lastAnimationProgress = progress
}
- private fun createAnimatedView(view: View): AnimatedView {
- val viewCenter = getViewCenter(view)
+ private fun createAnimatedView(view: View): AnimatedView =
+ AnimatedView(view = WeakReference(view)).updateAnimatedView(view)
+
+ private fun AnimatedView.updateAnimatedView(view: View): AnimatedView {
+ val viewCenter = Point()
+ viewCenterProvider.getViewCenter(view, viewCenter)
+
val viewCenterX = viewCenter.x
val viewCenterY = viewCenter.y
- val translationX: Float
- val translationY: Float
-
if (isVerticalFold) {
val distanceFromScreenCenterToViewCenter = screenSize.x / 2 - viewCenterX
- translationX = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
- translationY = 0f
+ startTranslationX = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ startTranslationY = 0f
} else {
val distanceFromScreenCenterToViewCenter = screenSize.y / 2 - viewCenterY
- translationX = 0f
- translationY = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ startTranslationX = 0f
+ startTranslationY = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
}
- return AnimatedView(
- view = WeakReference(view),
- startTranslationX = translationX,
- startTranslationY = translationY
- )
- }
-
- private fun getViewCenter(view: View): Point {
- val viewLocation = IntArray(2)
- view.getLocationOnScreen(viewLocation)
-
- val viewX = viewLocation[0]
- val viewY = viewLocation[1]
-
- val outPoint = Point()
- outPoint.x = viewX + view.width / 2
- outPoint.y = viewY + view.height / 2
-
- return outPoint
+ return this
}
/**
@@ -139,10 +146,29 @@
}
}
+ /**
+ * Interface that allows to use custom logic to get the center of the view
+ */
+ interface ViewCenterProvider {
+ /**
+ * Called when we need to get the center of the view
+ */
+ fun getViewCenter(view: View, outPoint: Point) {
+ val viewLocation = IntArray(2)
+ view.getLocationOnScreen(viewLocation)
+
+ val viewX = viewLocation[0]
+ val viewY = viewLocation[1]
+
+ outPoint.x = viewX + view.width / 2
+ outPoint.y = viewY + view.height / 2
+ }
+ }
+
private class AnimatedView(
val view: WeakReference<View>,
- val startTranslationX: Float,
- val startTranslationY: Float
+ var startTranslationX: Float = 0f,
+ var startTranslationY: Float = 0f
)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
index 58acce0..3d5998b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
@@ -16,7 +16,7 @@
package com.android.systemui.shared.communal;
-import android.view.SurfaceControlViewHost;
+import android.view.SurfaceControlViewHost.SurfacePackage;
/**
* An interface for receiving the result of a surface request. ICommunalSurfaceCallback is
@@ -26,5 +26,5 @@
/**
* Invoked when the CommunalSurface has generated the SurfacePackage to be displayed.
*/
- void onSurface(in SurfaceControlViewHost.SurfacePackage surfacePackage) = 1;
+ void onSurface(in SurfacePackage surfacePackage) = 1;
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 49cd279..b6be6ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -22,7 +22,6 @@
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
-import com.android.systemui.unfold.config.ANIMATION_MODE_HINGE_ANGLE
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
@@ -30,7 +29,6 @@
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
-import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,14 +48,8 @@
}
val hingeAngleProvider =
- if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
- // TODO: after removing temporary "config.mode" we should just
- // switch between fixed timing and hinge sensor based on this flag
- if (config.isHingeAngleEnabled) {
- HingeSensorAngleProvider(sensorManager)
- } else {
- RotationSensorHingeAngleProvider(sensorManager)
- }
+ if (config.isHingeAngleEnabled) {
+ HingeSensorAngleProvider(sensorManager)
} else {
EmptyHingeAngleProvider()
}
@@ -70,7 +62,7 @@
mainExecutor
)
- return if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+ return if (config.isHingeAngleEnabled) {
PhysicsBasedUnfoldTransitionProgressProvider(
mainHandler,
foldStateProvider
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index e7c6998a..3f027e3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -23,17 +23,16 @@
) : UnfoldTransitionConfig {
override val isEnabled: Boolean
- get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
+ get() = readIsEnabledResource() && isPropertyEnabled
override val isHingeAngleEnabled: Boolean
get() = readIsHingeAngleEnabled()
- @AnimationMode
- override val mode: Int
+ private val isPropertyEnabled: Boolean
get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
- ANIMATION_MODE_FIXED_TIMING)
+ UNFOLD_TRANSITION_PROPERTY_ENABLED) == UNFOLD_TRANSITION_PROPERTY_ENABLED
- private fun readIsEnabled(): Boolean = context.resources
+ private fun readIsEnabledResource(): Boolean = context.resources
.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
private fun readIsHingeAngleEnabled(): Boolean = context.resources
@@ -44,4 +43,5 @@
* Temporary persistent property to control unfold transition mode
* See [com.android.unfold.config.AnimationMode]
*/
-private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_mode"
+private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled"
+private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index a569757..5b187b3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -15,25 +15,7 @@
*/
package com.android.systemui.unfold.config
-import android.annotation.IntDef
-
interface UnfoldTransitionConfig {
val isEnabled: Boolean
val isHingeAngleEnabled: Boolean
-
- @AnimationMode
- val mode: Int
}
-
-@IntDef(prefix = ["ANIMATION_MODE_"], value = [
- ANIMATION_MODE_DISABLED,
- ANIMATION_MODE_FIXED_TIMING,
- ANIMATION_MODE_HINGE_ANGLE
-])
-
-@Retention(AnnotationRetention.SOURCE)
-annotation class AnimationMode
-
-const val ANIMATION_MODE_DISABLED = 0
-const val ANIMATION_MODE_FIXED_TIMING = 1
-const val ANIMATION_MODE_HINGE_ANGLE = 2
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
deleted file mode 100644
index 8b6eecf..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.android.systemui.unfold.updates.hinge
-
-import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
-import android.hardware.SensorManager
-import androidx.core.util.Consumer
-import com.android.systemui.shared.recents.utilities.Utilities
-
-/**
- * Temporary hinge angle provider that uses rotation sensor instead.
- * It requires to have the device in a certain position to work correctly
- * (flat to the ground)
- */
-internal class RotationSensorHingeAngleProvider(
- private val sensorManager: SensorManager
-) : HingeAngleProvider {
-
- private val sensorListener = HingeAngleSensorListener()
- private val listeners: MutableList<Consumer<Float>> = arrayListOf()
-
- override fun start() {
- val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
- sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
- }
-
- override fun stop() {
- sensorManager.unregisterListener(sensorListener)
- }
-
- override fun removeCallback(listener: Consumer<Float>) {
- listeners.remove(listener)
- }
-
- override fun addCallback(listener: Consumer<Float>) {
- listeners.add(listener)
- }
-
- private fun onHingeAngle(angle: Float) {
- listeners.forEach { it.accept(angle) }
- }
-
- private inner class HingeAngleSensorListener : SensorEventListener {
-
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
- }
-
- override fun onSensorChanged(event: SensorEvent) {
- // Jumbojack sends incorrect sensor reading 1.0f event in the beginning, let's ignore it
- if (event.values[3] == 1.0f) return
-
- val angleRadians = event.values.convertToAngle()
- val hingeAngleDegrees = Math.toDegrees(angleRadians).toFloat()
- val angle = Utilities.clamp(hingeAngleDegrees, FULLY_CLOSED_DEGREES, FULLY_OPEN_DEGREES)
- onHingeAngle(angle)
- }
-
- private val rotationMatrix = FloatArray(9)
- private val resultOrientation = FloatArray(9)
-
- private fun FloatArray.convertToAngle(): Double {
- SensorManager.getRotationMatrixFromVector(rotationMatrix, this)
- SensorManager.getOrientation(rotationMatrix, resultOrientation)
- return resultOrientation[2] + Math.PI
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 92f89d6..efcf40a 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -160,9 +160,7 @@
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mBatteryController.removeCallback(mBatteryCallback);
- if (!mView.isAttachedToWindow()) {
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
- }
+ mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
}
/** Animate the clock appearance */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 26059068..64d214d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -54,6 +54,7 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.ArrayList;
@@ -472,6 +473,11 @@
mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
: Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+ int keyguardState = mIsSecurityViewLeftAligned
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
+
updateSecurityViewLocation(animate);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0df2d65..9d649e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -151,9 +151,17 @@
}
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
+ if (canUseOneHandedBouncer()) {
+ bouncerSide = isOneHandedKeyguardLeftAligned()
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__RIGHT;
+ }
+
if (success) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS,
+ bouncerSide);
mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
// Force a garbage collection in an attempt to erase any lockscreen password left in
// memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
@@ -168,7 +176,8 @@
});
} else {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE,
+ bouncerSide);
reportFailedUnlockAttempt(userId, timeoutMs);
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
@@ -314,6 +323,14 @@
@Override
public void onResume(int reason) {
if (mCurrentSecurityMode != SecurityMode.None) {
+ int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
+ if (canUseOneHandedBouncer()) {
+ state = mView.isOneHandedModeLeftAligned()
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_RIGHT;
+ }
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, state);
+
getCurrentSecurityController().onResume(reason);
}
mView.onResume(
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 2a4022c..a115195 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -27,6 +27,7 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.media.AudioAttributes;
import android.os.Process;
@@ -330,8 +331,9 @@
private void updateLockIconLocation() {
if (mUdfpsSupported) {
FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
- mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
- props.sensorRadius);
+ final SensorLocationInternal location = props.getLocation();
+ mView.setCenterLocation(new PointF(location.sensorLocationX, location.sensorLocationY),
+ location.sensorRadius);
} else {
mView.setCenterLocation(
new PointF(mWidthPixels / 2,
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
index 3a0357d..4331f52 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
@@ -16,7 +16,8 @@
package com.android.keyguard.dagger;
-import com.android.systemui.statusbar.phone.UserAvatarView;
+import android.widget.FrameLayout;
+
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import dagger.BindsInstance;
@@ -31,8 +32,7 @@
/** Simple factory for {@link KeyguardUserSwitcherComponent}. */
@Subcomponent.Factory
interface Factory {
- KeyguardQsUserSwitchComponent build(
- @BindsInstance UserAvatarView userAvatarView);
+ KeyguardQsUserSwitchComponent build(@BindsInstance FrameLayout userAvatarContainer);
}
/** Builds a {@link KeyguardQsUserSwitchController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 186917f..d325b92 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,18 +16,13 @@
package com.android.systemui;
-import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -48,20 +43,15 @@
private static final String
ACTION_FACE_WAKE =
"com.android.systemui.latency.ACTION_FACE_WAKE";
- private static final String
- ACTION_TURN_ON_SCREEN =
- "com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
private final BiometricUnlockController mBiometricUnlockController;
- private final PowerManager mPowerManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@Inject
public LatencyTester(Context context, BiometricUnlockController biometricUnlockController,
- PowerManager powerManager, BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher) {
super(context);
mBiometricUnlockController = biometricUnlockController;
- mPowerManager = powerManager;
mBroadcastDispatcher = broadcastDispatcher;
}
@@ -74,7 +64,6 @@
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_FINGERPRINT_WAKE);
filter.addAction(ACTION_FACE_WAKE);
- filter.addAction(ACTION_TURN_ON_SCREEN);
mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -83,22 +72,11 @@
fakeWakeAndUnlock(BiometricSourceType.FINGERPRINT);
} else if (ACTION_FACE_WAKE.equals(action)) {
fakeWakeAndUnlock(BiometricSourceType.FACE);
- } else if (ACTION_TURN_ON_SCREEN.equals(action)) {
- fakeTurnOnScreen();
}
}
}, filter);
}
- private void fakeTurnOnScreen() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(
- LatencyTracker.ACTION_TURN_ON_SCREEN);
- }
- mPowerManager.wakeUp(
- SystemClock.uptimeMillis(), WAKE_REASON_UNKNOWN, "android.policy:LATENCY_TESTS");
- }
-
private void fakeWakeAndUnlock(BiometricSourceType type) {
mBiometricUnlockController.onBiometricAcquired(type);
mBiometricUnlockController.onBiometricAuthenticated(
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 66085ac0..39088c3 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -25,6 +25,7 @@
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -164,6 +165,12 @@
updateShowPercent();
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updatePercentView();
+ }
+
public void setColorsFromContext(Context context) {
if (context == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0790af9..71445a7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -114,7 +114,7 @@
@VisibleForTesting
IBiometricSysuiReceiver mReceiver;
@VisibleForTesting
- @NonNull final BiometricOrientationEventListener mOrientationListener;
+ @NonNull final BiometricDisplayListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -459,13 +459,15 @@
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray();
- mOrientationListener = new BiometricOrientationEventListener(context,
+ mOrientationListener = new BiometricDisplayListener(
+ context,
+ displayManager,
+ handler,
+ BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> {
onOrientationChanged();
return Unit.INSTANCE;
- },
- displayManager,
- handler);
+ });
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 9c818ff..f057bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -40,6 +40,7 @@
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
/***
* Controls the ripple effect that shows when authentication is successful.
@@ -57,6 +58,7 @@
private val bypassController: KeyguardBypassController,
private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
+ private val statusBarStateController: StatusBarStateController,
rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
var fingerprintSensorLocation: PointF? = null
@@ -64,8 +66,11 @@
private var circleReveal: LightRevealEffect? = null
private var udfpsController: UdfpsController? = null
+
private var dwellScale = 2f
private var expandedDwellScale = 2.5f
+ private var aodDwellScale = 1.9f
+ private var aodExpandedDwellScale = 2.3f
private var udfpsRadius: Float = -1f
override fun onInit() {
@@ -163,6 +168,22 @@
Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
}
+ private fun showDwellRipple() {
+ if (statusBarStateController.isDozing) {
+ mView.startDwellRipple(
+ /* startRadius */ udfpsRadius,
+ /* endRadius */ udfpsRadius * aodDwellScale,
+ /* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
+ /* isDozing */ true)
+ } else {
+ mView.startDwellRipple(
+ /* startRadius */ udfpsRadius,
+ /* endRadius */ udfpsRadius * dwellScale,
+ /* expandedRadius */ udfpsRadius * expandedDwellScale,
+ /* isDozing */ false)
+ }
+ }
+
private val keyguardUpdateMonitorCallback =
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
@@ -204,10 +225,7 @@
}
mView.setSensorLocation(fingerprintSensorLocation!!)
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * dwellScale,
- /* expandedRadius */ udfpsRadius * expandedDwellScale)
+ showDwellRipple()
}
override fun onFingerUp() {
@@ -223,7 +241,7 @@
private fun updateUdfpsDependentParams() {
authController.udfpsProps?.let {
if (it.size > 0) {
- udfpsRadius = it[0].sensorRadius.toFloat()
+ udfpsRadius = it[0].location.sensorRadius.toFloat()
udfpsController = udfpsControllerProvider.get()
if (mView.isAttachedToWindow) {
@@ -234,14 +252,18 @@
}
inner class AuthRippleCommand : Command {
- fun printDwellInfo(pw: PrintWriter) {
- pw.println("dwell ripple: " +
+ fun printLockScreenDwellInfo(pw: PrintWriter) {
+ pw.println("lock screen dwell ripple: " +
"\n\tsensorLocation=$fingerprintSensorLocation" +
"\n\tdwellScale=$dwellScale" +
- "\n\tdwellAlpha=${mView.dwellAlpha}, " +
- "duration=${mView.dwellAlphaDuration}" +
- "\n\tdwellExpand=$expandedDwellScale" +
- "\n\t(crash systemui to reset to default)")
+ "\n\tdwellExpand=$expandedDwellScale")
+ }
+
+ fun printAodDwellInfo(pw: PrintWriter) {
+ pw.println("aod dwell ripple: " +
+ "\n\tsensorLocation=$fingerprintSensorLocation" +
+ "\n\tdwellScale=$aodDwellScale" +
+ "\n\tdwellExpand=$aodExpandedDwellScale")
}
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -249,40 +271,12 @@
invalidCommand(pw)
} else {
when (args[0]) {
- "dwellScale" -> {
- if (args.size > 1 && args[1].toFloatOrNull() != null) {
- dwellScale = args[1].toFloat()
- printDwellInfo(pw)
+ "dwell" -> {
+ showDwellRipple()
+ if (statusBarStateController.isDozing) {
+ printAodDwellInfo(pw)
} else {
- pw.println("expected float argument <dwellScale>")
- }
- }
- "dwellAlpha" -> {
- if (args.size > 2 && args[1].toFloatOrNull() != null &&
- args[2].toLongOrNull() != null) {
- mView.dwellAlpha = args[1].toFloat()
- if (args[2].toFloat() > 200L) {
- pw.println("alpha animation duration must be less than 200ms.")
- }
- mView.dwellAlphaDuration = kotlin.math.min(args[2].toLong(), 200L)
- printDwellInfo(pw)
- } else {
- pw.println("expected two float arguments:" +
- " <dwellAlpha> <dwellAlphaDuration>")
- }
- }
- "dwellExpand" -> {
- if (args.size > 1 && args[1].toFloatOrNull() != null) {
- val expandedScale = args[1].toFloat()
- if (expandedScale <= dwellScale) {
- pw.println("invalid expandedScale. must be greater than " +
- "dwellScale=$dwellScale, but given $expandedScale")
- } else {
- expandedDwellScale = expandedScale
- }
- printDwellInfo(pw)
- } else {
- pw.println("expected float argument <expandedScale>")
+ printLockScreenDwellInfo(pw)
}
}
"fingerprint" -> {
@@ -313,9 +307,7 @@
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
pw.println("Available commands:")
- pw.println(" dwellScale <200ms_scale: float>")
- pw.println(" dwellAlpha <alpha: float> <duration : long>")
- pw.println(" dwellExpand <expanded_scale: float>")
+ pw.println(" dwell")
pw.println(" fingerprint")
pw.println(" face")
pw.println(" custom <x-location: int> <y-location: int>")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 8e13037..1113579 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -45,12 +45,18 @@
*/
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
- private val dwellPulseDuration = 200L
- var dwellAlphaDuration = dwellPulseDuration
- private val dwellExpandDuration = 1200L - dwellPulseDuration
- private val retractDuration = 400L
- var dwellAlpha: Float = .5f
+ private val dwellPulseDuration = 50L
+ private val dwellAlphaDuration = dwellPulseDuration
+ private val dwellAlpha: Float = 1f
+ private val dwellExpandDuration = 1200L - dwellPulseDuration
+
+ private val aodDwellPulseDuration = 50L
+ private var aodDwellAlphaDuration = aodDwellPulseDuration
+ private var aodDwellAlpha: Float = .8f
+ private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
+
+ private val retractDuration = 400L
private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false
private val rippleShader = RippleShader()
@@ -142,7 +148,12 @@
* Ripple that moves animates from an outer ripple ring of
* startRadius => endRadius => expandedRadius
*/
- fun startDwellRipple(startRadius: Float, endRadius: Float, expandedRadius: Float) {
+ fun startDwellRipple(
+ startRadius: Float,
+ endRadius: Float,
+ expandedRadius: Float,
+ isDozing: Boolean
+ ) {
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
return
}
@@ -153,12 +164,13 @@
val endInitialDwellProgress = endRadius / radius / 4f
val endExpandDwellProgress = expandedRadius / radius / 4f
- val pulseOutEndAlpha = (255 * dwellAlpha).toInt()
- val expandDwellEndAlpha = kotlin.math.min((255 * (dwellAlpha + .25f)).toInt(), 255)
+ val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
+ val pulseOutEndAlpha = (255 * alpha).toInt()
+ val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
endInitialDwellProgress).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = dwellPulseDuration
+ duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float
@@ -170,7 +182,7 @@
val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
interpolator = Interpolators.LINEAR
- duration = dwellAlphaDuration
+ duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
@@ -184,7 +196,7 @@
val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
endExpandDwellProgress).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = dwellExpandDuration
+ duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float
@@ -197,7 +209,7 @@
val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
.apply {
interpolator = Interpolators.LINEAR
- duration = dwellExpandDuration
+ duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
new file mode 100644
index 0000000..b7404df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Handler
+import android.view.Surface
+import com.android.systemui.biometrics.BiometricDisplayListener.SensorType.Generic
+
+/**
+ * A listener for keeping overlays for biometric sensors aligned with the physical device
+ * device's screen. The [onChanged] will be dispatched on the [handler]
+ * whenever a relevant change to the device's configuration (orientation, fold, display change,
+ * etc.) may require the UI to change for the given [sensorType].
+ */
+class BiometricDisplayListener(
+ private val context: Context,
+ private val displayManager: DisplayManager,
+ private val handler: Handler,
+ private val sensorType: SensorType = SensorType.Generic,
+ private val onChanged: () -> Unit
+) : DisplayManager.DisplayListener {
+
+ private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
+
+ override fun onDisplayAdded(displayId: Int) {}
+ override fun onDisplayRemoved(displayId: Int) {}
+ override fun onDisplayChanged(displayId: Int) {
+ val rotationChanged = didRotationChange()
+
+ when (sensorType) {
+ is SensorType.SideFingerprint -> onChanged()
+ else -> {
+ if (rotationChanged) {
+ onChanged()
+ }
+ }
+ }
+ }
+
+ private fun didRotationChange(): Boolean {
+ val rotation = context.display?.rotation ?: return false
+ val last = lastRotation
+ lastRotation = rotation
+ return last != rotation
+ }
+
+ /** Listen for changes. */
+ fun enable() {
+ displayManager.registerDisplayListener(this, handler)
+ }
+
+ /** Stop listening for changes. */
+ fun disable() {
+ displayManager.unregisterDisplayListener(this)
+ }
+
+ /**
+ * Type of sensor to determine what kind of display changes require layouts.
+ *
+ * The [Generic] type should be used in cases where the modality can vary, such as
+ * biometric prompt (and this object will likely change as multi-mode auth is added).
+ */
+ sealed class SensorType {
+ object Generic : SensorType()
+ data class UnderDisplayFingerprint(
+ val properties: FingerprintSensorPropertiesInternal
+ ) : SensorType()
+ data class SideFingerprint(
+ val properties: FingerprintSensorPropertiesInternal
+ ) : SensorType()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
deleted file mode 100644
index 98a03a1..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.hardware.display.DisplayManager
-import android.os.Handler
-import android.view.OrientationEventListener
-import android.view.Surface
-
-/**
- * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever
- * the orientation of the device has changed in order to keep overlays for biometric sensors
- * aligned with the device's screen.
- */
-class BiometricOrientationEventListener(
- private val context: Context,
- private val onOrientationChanged: () -> Unit,
- private val displayManager: DisplayManager,
- private val handler: Handler
-) : DisplayManager.DisplayListener {
-
- private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
-
- override fun onDisplayAdded(displayId: Int) {}
- override fun onDisplayRemoved(displayId: Int) {}
- override fun onDisplayChanged(displayId: Int) {
- val rotation = context.display?.rotation ?: return
- if (lastRotation != rotation) {
- lastRotation = rotation
-
- onOrientationChanged()
- }
- }
-
- fun enable() {
- displayManager.registerDisplayListener(this, handler)
- }
-
- fun disable() {
- displayManager.unregisterDisplayListener(this)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
deleted file mode 100644
index 8f6e249..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.ISidefpsController;
-import android.os.Handler;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import javax.inject.Inject;
-
-import kotlin.Unit;
-
-/**
- * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
- */
-@SysUISingleton
-public class SidefpsController {
- private static final String TAG = "SidefpsController";
- @NonNull private final Context mContext;
- @NonNull private final LayoutInflater mInflater;
- private final FingerprintManager mFingerprintManager;
- private final WindowManager mWindowManager;
- private final DelayableExecutor mFgExecutor;
- @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
-
- // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices
- private final int mDisplayHeight;
- private final int mDisplayWidth;
-
- private boolean mIsVisible = false;
- @Nullable private SidefpsView mView;
-
- static final int SFPS_AFFORDANCE_WIDTH = 50; // in default portrait mode
-
- @NonNull
- private final ISidefpsController mSidefpsControllerImpl = new ISidefpsController.Stub() {
- @Override
- public void show() {
- mFgExecutor.execute(() -> {
- SidefpsController.this.show();
- mIsVisible = true;
- });
- }
-
- @Override
- public void hide() {
- mFgExecutor.execute(() -> {
- SidefpsController.this.hide();
- mIsVisible = false;
- });
- }
- };
-
- @VisibleForTesting
- final FingerprintSensorPropertiesInternal mSensorProps;
- private final WindowManager.LayoutParams mCoreLayoutParams;
-
- @Inject
- public SidefpsController(@NonNull Context context,
- @NonNull LayoutInflater inflater,
- @Nullable FingerprintManager fingerprintManager,
- @NonNull WindowManager windowManager,
- @Main DelayableExecutor fgExecutor,
- @NonNull DisplayManager displayManager,
- @Main Handler handler) {
- mContext = context;
- mInflater = inflater;
- mFingerprintManager = checkNotNull(fingerprintManager);
- mWindowManager = windowManager;
- mFgExecutor = fgExecutor;
- mOrientationListener = new BiometricOrientationEventListener(
- context,
- () -> {
- onOrientationChanged();
- return Unit.INSTANCE;
- },
- displayManager,
- handler);
-
- mSensorProps = findFirstSidefps();
- checkArgument(mSensorProps != null);
-
- mCoreLayoutParams = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- getCoreLayoutParamFlags(),
- PixelFormat.TRANSLUCENT);
- mCoreLayoutParams.setTitle(TAG);
- // Overrides default, avoiding status bars during layout
- mCoreLayoutParams.setFitInsetsTypes(0);
- mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
- mCoreLayoutParams.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-
- DisplayMetrics displayMetrics = new DisplayMetrics();
- windowManager.getDefaultDisplay().getMetrics(displayMetrics);
- mDisplayHeight = displayMetrics.heightPixels;
- mDisplayWidth = displayMetrics.widthPixels;
-
- mFingerprintManager.setSidefpsController(mSidefpsControllerImpl);
- }
-
- private void show() {
- mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false);
- mView.setSensorProperties(mSensorProps);
- mWindowManager.addView(mView, computeLayoutParams());
-
- mOrientationListener.enable();
- }
-
- private void hide() {
- if (mView != null) {
- mWindowManager.removeView(mView);
- mView.setOnTouchListener(null);
- mView.setOnHoverListener(null);
- mView = null;
- } else {
- Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
- }
-
- mOrientationListener.disable();
- }
-
- private void onOrientationChanged() {
- // If mView is null or if view is hidden, then return.
- if (mView == null || !mIsVisible) {
- return;
- }
-
- // If the overlay needs to be displayed with a new configuration, destroy the current
- // overlay, and re-create and show the overlay with the updated LayoutParams.
- hide();
- show();
- }
-
- @Nullable
- private FingerprintSensorPropertiesInternal findFirstSidefps() {
- for (FingerprintSensorPropertiesInternal props :
- mFingerprintManager.getSensorPropertiesInternal()) {
- if (props.isAnySidefpsType()) {
- // TODO(b/188690214): L155-L173 can be removed once sensorLocationX,
- // sensorLocationY, and sensorRadius are defined in sensorProps by the HAL
- int sensorLocationX = 25;
- int sensorLocationY = 610;
- int sensorRadius = 112;
-
- FingerprintSensorPropertiesInternal tempProps =
- new FingerprintSensorPropertiesInternal(
- props.sensorId,
- props.sensorStrength,
- props.maxEnrollmentsPerUser,
- props.componentInfo,
- props.sensorType,
- props.resetLockoutRequiresHardwareAuthToken,
- sensorLocationX,
- sensorLocationY,
- sensorRadius
- );
- props = tempProps;
- return props;
- }
- }
- return null;
- }
-
- private int getCoreLayoutParamFlags() {
- return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
-
- /**
- * Computes layout params depending on orientation & folding configuration of device
- */
- private WindowManager.LayoutParams computeLayoutParams() {
- mCoreLayoutParams.flags = getCoreLayoutParamFlags();
- // Y value of top of affordance in portrait mode, X value of left of affordance in landscape
- int sfpsLocationY = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- int sfpsAffordanceHeight = mSensorProps.sensorRadius * 2;
-
- // Calculate coordinates of drawable area for the fps affordance, accounting for orientation
- switch (mContext.getDisplay().getRotation()) {
- case Surface.ROTATION_90:
- mCoreLayoutParams.x = sfpsLocationY;
- mCoreLayoutParams.y = 0;
- mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.width = sfpsAffordanceHeight;
- break;
- case Surface.ROTATION_270:
- mCoreLayoutParams.x = mDisplayHeight - sfpsLocationY - sfpsAffordanceHeight;
- mCoreLayoutParams.y = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.width = sfpsAffordanceHeight;
- break;
- default: // Portrait
- mCoreLayoutParams.x = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.y = sfpsLocationY;
- mCoreLayoutParams.height = sfpsAffordanceHeight;
- mCoreLayoutParams.width = SFPS_AFFORDANCE_WIDTH;
- }
- return mCoreLayoutParams;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
new file mode 100644
index 0000000..c0731b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.os.Handler
+import android.util.Log
+import android.view.Display
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.RawRes
+import com.airbnb.lottie.LottieAnimationView
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+private const val TAG = "SidefpsController"
+
+/**
+ * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
+ */
+@SysUISingleton
+class SidefpsController @Inject constructor(
+ private val context: Context,
+ private val layoutInflater: LayoutInflater,
+ fingerprintManager: FingerprintManager?,
+ private val windowManager: WindowManager,
+ @Main mainExecutor: DelayableExecutor,
+ displayManager: DisplayManager,
+ @Main handler: Handler
+) {
+ @VisibleForTesting
+ val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
+ ?.sensorPropertiesInternal
+ ?.firstOrNull { it.isAnySidefpsType }
+ ?: throw IllegalStateException("no side fingerprint sensor")
+
+ @VisibleForTesting
+ val orientationListener = BiometricDisplayListener(
+ context,
+ displayManager,
+ handler,
+ BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
+ ) { onOrientationChanged() }
+
+ private var overlayView: View? = null
+ set(value) {
+ field?.let { oldView ->
+ windowManager.removeView(oldView)
+ orientationListener.disable()
+ }
+ field = value
+ field?.let { newView ->
+ windowManager.addView(newView, overlayViewParams)
+ orientationListener.enable()
+ }
+ }
+
+ private val overlayViewParams = WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT
+ ).apply {
+ title = TAG
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ }
+
+ init {
+ fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() {
+ override fun show() = mainExecutor.execute {
+ if (overlayView == null) {
+ overlayView = createOverlayForDisplay()
+ } else {
+ Log.v(TAG, "overlay already shown")
+ }
+ }
+ override fun hide() = mainExecutor.execute { overlayView = null }
+ })
+ }
+
+ private fun onOrientationChanged() {
+ if (overlayView != null) {
+ overlayView = createOverlayForDisplay()
+ }
+ }
+
+ private fun createOverlayForDisplay(): View {
+ val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+ val display = context.display!!
+
+ val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
+ lottie.setAnimation(display.asSideFpsAnimation())
+ view.rotation = display.asSideFpsAnimationRotation()
+
+ updateOverlayParams(display, lottie.composition?.bounds ?: Rect())
+ lottie.addLottieOnCompositionLoadedListener {
+ if (overlayView == view) {
+ updateOverlayParams(display, it.bounds)
+ windowManager.updateViewLayout(overlayView, overlayViewParams)
+ }
+ }
+
+ return view
+ }
+
+ private fun updateOverlayParams(display: Display, bounds: Rect) {
+ val isPortrait = display.isPortrait()
+ val size = windowManager.maximumWindowMetrics.bounds
+ val displayWidth = if (isPortrait) size.width() else size.height()
+ val displayHeight = if (isPortrait) size.height() else size.width()
+ val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
+ if (location == null) {
+ Log.w(TAG, "No location specified for display: ${display.uniqueId}")
+ }
+ location ?: sensorProps.location
+ }
+
+ // ignore sensorLocationX and sensorRadius since it's assumed to be on the side
+ // of the device and centered at sensorLocationY
+ val (x, y) = when (display.rotation) {
+ Surface.ROTATION_90 ->
+ Pair(offsets.sensorLocationY, 0)
+ Surface.ROTATION_270 ->
+ Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth)
+ Surface.ROTATION_180 ->
+ Pair(0, displayHeight - offsets.sensorLocationY - bounds.height())
+ else ->
+ Pair(displayWidth, offsets.sensorLocationY)
+ }
+ overlayViewParams.x = x
+ overlayViewParams.y = y
+ }
+}
+
+@RawRes
+private fun Display.asSideFpsAnimation(): Int = when (rotation) {
+ Surface.ROTATION_0 -> R.raw.sfps_pulse
+ Surface.ROTATION_180 -> R.raw.sfps_pulse
+ else -> R.raw.sfps_pulse_landscape
+}
+
+private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) {
+ Surface.ROTATION_180 -> 180f
+ Surface.ROTATION_270 -> 180f
+ else -> 0f
+}
+
+private fun Display.isPortrait(): Boolean =
+ rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
deleted file mode 100644
index 4fc59d6..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.biometrics.SidefpsController.SFPS_AFFORDANCE_WIDTH;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.util.AttributeSet;
-import android.view.Surface;
-import android.widget.FrameLayout;
-
-/**
- * A view containing a normal drawable view for sidefps events.
- */
-public class SidefpsView extends FrameLayout {
- private static final String TAG = "SidefpsView";
- private static final int POINTER_SIZE_PX = 50;
- private static final int ROUND_RADIUS = 15;
-
- @NonNull private final RectF mSensorRect;
- @NonNull private final Paint mSensorRectPaint;
- @NonNull private final Paint mPointerText;
- @NonNull private final Context mContext;
-
- // Used to obtain the sensor location.
- @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
- @Surface.Rotation private int mOrientation;
-
- public SidefpsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- super.setWillNotDraw(false);
- mContext = context;
- mPointerText = new Paint(0 /* flags */);
- mPointerText.setAntiAlias(true);
- mPointerText.setColor(Color.WHITE);
- mPointerText.setTextSize(POINTER_SIZE_PX);
-
- mSensorRect = new RectF();
- mSensorRectPaint = new Paint(0 /* flags */);
- mSensorRectPaint.setAntiAlias(true);
- mSensorRectPaint.setColor(Color.BLUE); // TODO: Fix Color
- mSensorRectPaint.setStyle(Paint.Style.FILL);
- }
-
- void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
- mSensorProps = properties;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawRoundRect(mSensorRect, ROUND_RADIUS, ROUND_RADIUS, mSensorRectPaint);
- int x, y;
- if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
- x = mSensorProps.sensorRadius + 10;
- y = SFPS_AFFORDANCE_WIDTH / 2 + 15;
- } else {
- x = SFPS_AFFORDANCE_WIDTH / 2 - 10;
- y = mSensorProps.sensorRadius + 30;
- }
- canvas.drawText(
- ">",
- x,
- y,
- mPointerText
- );
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mOrientation = mContext.getDisplay().getRotation();
- if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
- right = mSensorProps.sensorRadius * 2;
- bottom = SFPS_AFFORDANCE_WIDTH;
- } else {
- right = SFPS_AFFORDANCE_WIDTH;
- bottom = mSensorProps.sensorRadius * 2;
- }
-
- mSensorRect.set(
- 0,
- 0,
- right,
- bottom);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 62b9fd4..2ca6ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,7 +16,7 @@
package com.android.systemui.biometrics;
-import static android.hardware.fingerprint.IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD;
+import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -32,6 +32,8 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RectF;
+import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -39,7 +41,6 @@
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.media.AudioAttributes;
import android.os.Handler;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -63,7 +64,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,6 +71,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -93,7 +94,7 @@
* controls/manages all UDFPS sensors. In other words, a single controller is registered with
* {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
* as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or
- * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
+ * {@link IUdfpsOverlayController#showUdfpsOverlay(int)} should all have
* {@code sensorId} parameters.
*/
@SuppressWarnings("deprecation")
@@ -117,9 +118,7 @@
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@Nullable private final Vibrator mVibrator;
- @NonNull private final Handler mMainHandler;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -128,7 +127,9 @@
@NonNull private final KeyguardBypassController mKeyguardBypassController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
- @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
+ @NonNull private final UnlockedScreenOffAnimationController
+ mUnlockedScreenOffAnimationController;
+ @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -241,8 +242,8 @@
@NonNull IUdfpsOverlayControllerCallback callback) {
mFgExecutor.execute(() -> {
final UdfpsEnrollHelper enrollHelper;
- if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
- || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ if (reason == BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
+ || reason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING) {
enrollHelper = new UdfpsEnrollHelper(mContext, reason);
} else {
enrollHelper = null;
@@ -320,7 +321,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (mServerRequest != null
- && mServerRequest.mRequestReason != REASON_AUTH_FPM_KEYGUARD
+ && mServerRequest.mRequestReason != REASON_AUTH_KEYGUARD
&& Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, mRequestReason: "
+ mServerRequest.mRequestReason);
@@ -517,7 +518,6 @@
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@@ -531,11 +531,11 @@
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@NonNull ConfigurationController configurationController,
- @NonNull SystemClock systemClock) {
+ @NonNull SystemClock systemClock,
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
mContext = context;
mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
- mMainHandler = new Handler(Looper.getMainLooper());
mVibrator = vibrator;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -549,7 +549,6 @@
mKeyguardViewManager = statusBarKeyguardViewManager;
mDumpManager = dumpManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mKeyguardViewMediator = keyguardViewMediator;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
@@ -557,21 +556,23 @@
mHbmProvider = hbmProvider.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
- mOrientationListener = new BiometricOrientationEventListener(
- context,
- () -> {
- onOrientationChanged();
- return Unit.INSTANCE;
- },
- displayManager,
- mainHandler);
mKeyguardBypassController = keyguardBypassController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
checkArgument(mSensorProps != null);
+ mOrientationListener = new BiometricDisplayListener(
+ context,
+ displayManager,
+ mainHandler,
+ new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps),
+ () -> {
+ onOrientationChanged();
+ return Unit.INSTANCE;
+ });
mCoreLayoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
@@ -641,10 +642,11 @@
// on lockscreen and for the udfps light reveal animation on keyguard.
// Keyguard is only shown in portrait mode for now, so this will need to
// be updated if that ever changes.
- return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
+ final SensorLocationInternal location = mSensorProps.getLocation();
+ return new RectF(location.sensorLocationX - location.sensorRadius,
+ location.sensorLocationY - location.sensorRadius,
+ location.sensorLocationX + location.sensorRadius,
+ location.sensorLocationY + location.sensorRadius);
}
private void updateOverlay() {
@@ -657,6 +659,20 @@
}
}
+ private boolean shouldRotate(@Nullable UdfpsAnimationViewController animation) {
+ if (!(animation instanceof UdfpsKeyguardViewController)) {
+ // always rotate view if we're not on the keyguard
+ return true;
+ }
+
+ // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
+ if (mKeyguardUpdateMonitor.isGoingToSleep() || !mKeyguardStateController.isOccluded()) {
+ return false;
+ }
+
+ return true;
+ }
+
private WindowManager.LayoutParams computeLayoutParams(
@Nullable UdfpsAnimationViewController animation) {
final int paddingX = animation != null ? animation.getPaddingX() : 0;
@@ -668,10 +684,11 @@
}
// Default dimensions assume portrait mode.
- mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX;
- mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY;
- mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX;
- mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY;
+ final SensorLocationInternal location = mSensorProps.getLocation();
+ mCoreLayoutParams.x = location.sensorLocationX - location.sensorRadius - paddingX;
+ mCoreLayoutParams.y = location.sensorLocationY - location.sensorRadius - paddingY;
+ mCoreLayoutParams.height = 2 * location.sensorRadius + 2 * paddingX;
+ mCoreLayoutParams.width = 2 * location.sensorRadius + 2 * paddingY;
Point p = new Point();
// Gets the size based on the current rotation of the display.
@@ -680,24 +697,28 @@
// Transform dimensions if the device is in landscape mode
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
- if (animation instanceof UdfpsKeyguardViewController
- && mKeyguardUpdateMonitor.isGoingToSleep()) {
+ if (!shouldRotate(animation)) {
+ Log.v(TAG, "skip rotating udfps location ROTATION_90");
break;
+ } else {
+ Log.v(TAG, "rotate udfps location ROTATION_90");
}
- mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ mCoreLayoutParams.x = location.sensorLocationY - location.sensorRadius
- paddingX;
- mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ mCoreLayoutParams.y = p.y - location.sensorLocationX - location.sensorRadius
- paddingY;
break;
case Surface.ROTATION_270:
- if (animation instanceof UdfpsKeyguardViewController
- && mKeyguardUpdateMonitor.isGoingToSleep()) {
+ if (!shouldRotate(animation)) {
+ Log.v(TAG, "skip rotating udfps location ROTATION_270");
break;
+ } else {
+ Log.v(TAG, "rotate udfps location ROTATION_270");
}
- mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ mCoreLayoutParams.x = p.x - location.sensorLocationY - location.sensorRadius
- paddingX;
- mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ mCoreLayoutParams.y = location.sensorLocationX - location.sensorRadius
- paddingY;
break;
@@ -747,9 +768,9 @@
// This view overlaps the sensor area, so prevent it from being selectable
// during a11y.
- if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
- || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING
- || reason == IUdfpsOverlayController.REASON_AUTH_BP) {
+ if (reason == BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
+ || reason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING
+ || reason == BiometricOverlayConstants.REASON_AUTH_BP) {
mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@@ -767,8 +788,8 @@
private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
switch (reason) {
- case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+ case BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR:
+ case BiometricOverlayConstants.REASON_ENROLL_ENROLLING:
UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
R.layout.udfps_enroll_view, null);
mView.addView(enrollView);
@@ -780,7 +801,7 @@
mStatusBarOptional,
mDumpManager
);
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+ case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
mInflater.inflate(R.layout.udfps_keyguard_view, null);
mView.addView(keyguardView);
@@ -790,16 +811,15 @@
mStatusBarOptional,
mKeyguardViewManager,
mKeyguardUpdateMonitor,
- mFgExecutor,
mDumpManager,
- mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
this
);
- case IUdfpsOverlayController.REASON_AUTH_BP:
+ case BiometricOverlayConstants.REASON_AUTH_BP:
// note: empty controller, currently shows no visual affordance
UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
mView.addView(bpView);
@@ -809,7 +829,7 @@
mStatusBarOptional,
mDumpManager
);
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+ case BiometricOverlayConstants.REASON_AUTH_OTHER:
UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
mInflater.inflate(R.layout.udfps_fpm_other_view, null);
mView.addView(authOtherView);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 7ccfb86..6cc8acf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.Log;
import android.view.Surface;
@@ -93,7 +94,7 @@
// Go through each of the children and do the custom measurement.
int totalHeight = 0;
final int numChildren = mView.getChildCount();
- final int sensorDiameter = mSensorProps.sensorRadius * 2;
+ final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
for (int i = 0; i < numChildren; i++) {
final View child = mView.getChildAt(i);
if (child.getId() == R.id.biometric_icon_frame) {
@@ -160,7 +161,7 @@
final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
mSensorProps, displayWidth, dialogMargin, horizontalInset);
- final int sensorDiameter = mSensorProps.sensorRadius * 2;
+ final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
int remeasuredHeight = 0;
@@ -257,10 +258,10 @@
@NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
int navbarBottomInsetPx) {
-
+ final SensorLocationInternal location = sensorProperties.getLocation();
final int sensorDistanceFromBottom = displayHeightPx
- - sensorProperties.sensorLocationY
- - sensorProperties.sensorRadius;
+ - location.sensorLocationY
+ - location.sensorRadius;
final int spacerHeight = sensorDistanceFromBottom
- textIndicatorHeightPx
@@ -318,10 +319,10 @@
static int calculateHorizontalSpacerWidthForLandscape(
@NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
int dialogMarginPx, int navbarHorizontalInsetPx) {
-
+ final SensorLocationInternal location = sensorProperties.getLocation();
final int sensorDistanceFromEdge = displayWidthPx
- - sensorProperties.sensorLocationY
- - sensorProperties.sensorRadius;
+ - location.sensorLocationY
+ - location.sensorRadius;
final int horizontalPadding = sensorDistanceFromEdge
- dialogMarginPx
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 19148e3..c6d2192 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
-import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
@@ -119,7 +119,7 @@
}
boolean shouldShowProgressBar() {
- return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ return mEnrollReason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
}
void onEnrollmentProgress(int remaining) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index d9edef4..c83006d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -63,7 +63,7 @@
void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
- final int sensorHeight = sensorProps.sensorRadius * 2;
+ final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
final int sensorWidth = sensorHeight;
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
params.width = sensorWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8ce4c3b..c61aaf9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -26,16 +26,15 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
@@ -49,13 +48,13 @@
public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final DelayableExecutor mExecutor;
- @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final UdfpsController mUdfpsController;
+ @NonNull private final UnlockedScreenOffAnimationController
+ mUnlockedScreenOffAnimationController;
private boolean mShowingUdfpsBouncer;
private boolean mUdfpsRequested;
@@ -82,24 +81,22 @@
@NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull DelayableExecutor mainDelayableExecutor,
@NonNull DumpManager dumpManager,
- @NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock,
@NonNull KeyguardStateController keyguardStateController,
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBarOptional, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mExecutor = mainDelayableExecutor;
- mKeyguardViewMediator = keyguardViewMediator;
mLockScreenShadeTransitionController = transitionController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
mKeyguardStateController = keyguardStateController;
mUdfpsController = udfpsController;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
}
@Override
@@ -138,6 +135,7 @@
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
+ mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
}
@Override
@@ -156,6 +154,7 @@
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
+ mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
}
@Override
@@ -428,4 +427,7 @@
updatePauseAuth();
}
};
+
+ private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
+ (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 15f77ff..6d31ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -25,6 +25,7 @@
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Build;
import android.os.UserHandle;
@@ -141,11 +142,12 @@
: mAnimationViewController.getPaddingX();
int paddingY = mAnimationViewController == null ? 0
: mAnimationViewController.getPaddingY();
+ final SensorLocationInternal location = mSensorProps.getLocation();
mSensorRect.set(
paddingX,
paddingY,
- 2 * mSensorProps.sensorRadius + paddingX,
- 2 * mSensorProps.sensorRadius + paddingY);
+ 2 * location.sensorRadius + paddingX,
+ 2 * location.sensorRadius + paddingY);
if (mAnimationViewController != null) {
mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 4104e31..46a03e8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -16,6 +16,10 @@
package com.android.systemui.controls.ui
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
@@ -23,18 +27,25 @@
import android.view.WindowInsets.Type
import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.management.ControlsAnimations
import com.android.systemui.util.LifecycleActivity
import javax.inject.Inject
/**
- * Displays Device Controls inside an activity
+ * Displays Device Controls inside an activity. This activity is available to be displayed over the
+ * lockscreen if the user has allowed it via
+ * [android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS]. This activity will be
+ * destroyed on SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as
+ * user expectations for the activity to not continue running.
*/
class ControlsActivity @Inject constructor(
- private val uiController: ControlsUiController
+ private val uiController: ControlsUiController,
+ private val broadcastDispatcher: BroadcastDispatcher
) : LifecycleActivity() {
private lateinit var parent: ViewGroup
+ private lateinit var broadcastReceiver: BroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -62,6 +73,8 @@
WindowInsets.CONSUMED
}
}
+
+ initBroadcastReceiver()
}
override fun onResume() {
@@ -83,4 +96,25 @@
uiController.hide()
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+
+ broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+ }
+
+ private fun initBroadcastReceiver() {
+ broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ finish()
+ }
+ }
+ }
+
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_SCREEN_OFF)
+ broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 557bca8..5a52fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -256,13 +256,13 @@
// Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
- if (keyguardStateController.isUnlocked()) {
+ if (keyguardStateController.isShowing()) {
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
+ } else {
activityContext.startActivity(
intent,
ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
)
- } else {
- activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 4196465..845d7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -214,6 +214,14 @@
}
/**
+ * Appends display state delayed by UDFPS event to the logs
+ * @param delayedDisplayState the display screen state that was delayed
+ */
+ public void traceDisplayStateDelayedByUdfps(int delayedDisplayState) {
+ mLogger.logDisplayStateDelayedByUdfps(delayedDisplayState);
+ }
+
+ /**
* Appends display state changed event to the logs
* @param displayState new DozeMachine state
*/
@@ -267,6 +275,13 @@
}
/**
+ * Appends sensor event dropped event to logs
+ */
+ public void traceSensorEventDropped(int sensorEvent, String reason) {
+ mLogger.logSensorEventDropped(sensorEvent, reason);
+ }
+
+ /**
* Appends pulse dropped event to logs
* @param reason why the pulse was dropped
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 9bc74be..dc18618 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -160,6 +160,14 @@
})
}
+ fun logDisplayStateDelayedByUdfps(delayedDisplayState: Int) {
+ buffer.log(TAG, INFO, {
+ str1 = Display.stateToString(delayedDisplayState)
+ }, {
+ "Delaying display state change to: $str1 due to UDFPS activity"
+ })
+ }
+
fun logDisplayStateChanged(displayState: Int) {
buffer.log(TAG, INFO, {
str1 = Display.stateToString(displayState)
@@ -197,6 +205,15 @@
})
}
+ fun logSensorEventDropped(sensorEvent: Int, reason: String) {
+ buffer.log(TAG, INFO, {
+ int1 = sensorEvent
+ str1 = reason
+ }, {
+ "SensorEvent [$int1] dropped, reason=$str1"
+ })
+ }
+
fun logPulseDropped(reason: String) {
buffer.log(TAG, INFO, {
str1 = reason
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 98d2739..da7b389 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -29,7 +29,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.view.Display;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.BrightnessSensor;
@@ -111,7 +110,15 @@
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
+ resetBrightnessToDefault();
+ break;
+ case DOZE_AOD:
+ case DOZE_REQUEST_PULSE:
+ case DOZE_AOD_DOCKED:
+ setLightSensorEnabled(true);
+ break;
case DOZE:
+ setLightSensorEnabled(false);
resetBrightnessToDefault();
break;
case FINISH:
@@ -124,22 +131,6 @@
}
}
- @Override
- public void onScreenState(int state) {
- boolean isDockedScreenOn = state == Display.STATE_ON && mDockManager.isDocked();
- if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
- || (isDockedScreenOn && shouldRegisterLightSensorWhenScreenOnDocked())) {
- setLightSensorEnabled(true);
- } else {
- setLightSensorEnabled(false);
- }
- }
-
- private boolean shouldRegisterLightSensorWhenScreenOnDocked() {
- return !mDozeParameters.brightnessUsesProx()
- || !mDozeParameters.getSelectivelyRegisterSensorsUsingProx();
- }
-
private void onDestroy() {
setLightSensorEnabled(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 8c50a16..8f1486b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -26,6 +26,11 @@
import android.util.Log;
import android.view.Display;
+import androidx.annotation.Nullable;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
@@ -34,6 +39,7 @@
import com.android.systemui.util.wakelock.WakeLock;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Controls the screen when dozing.
@@ -56,23 +62,61 @@
*/
public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2500;
+ /**
+ * Add an extra delay to the transition to DOZE when udfps is current activated before
+ * the display state transitions from ON => DOZE.
+ */
+ public static final int UDFPS_DISPLAY_STATE_DELAY = 1200;
+
private final DozeMachine.Service mDozeService;
private final Handler mHandler;
private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
private final DozeParameters mParameters;
private final DozeHost mDozeHost;
+ private final AuthController mAuthController;
+ private final Provider<UdfpsController> mUdfpsControllerProvider;
+ @Nullable private UdfpsController mUdfpsController;
+ private final DozeLog mDozeLog;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
@Inject
- public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler,
- DozeHost host, DozeParameters parameters, WakeLock wakeLock) {
+ public DozeScreenState(
+ @WrappedService DozeMachine.Service service,
+ @Main Handler handler,
+ DozeHost host,
+ DozeParameters parameters,
+ WakeLock wakeLock,
+ AuthController authController,
+ Provider<UdfpsController> udfpsControllerProvider,
+ DozeLog dozeLog) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
mDozeHost = host;
mWakeLock = new SettableWakeLock(wakeLock, TAG);
+ mAuthController = authController;
+ mUdfpsControllerProvider = udfpsControllerProvider;
+ mDozeLog = dozeLog;
+
+ updateUdfpsController();
+ if (mUdfpsController == null) {
+ mAuthController.addCallback(new AuthController.Callback() {
+ @Override
+ public void onAllAuthenticatorsRegistered() {
+ updateUdfpsController();
+ }
+ });
+ }
+ }
+
+ private void updateUdfpsController() {
+ if (mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ mUdfpsController = mUdfpsControllerProvider.get();
+ } else {
+ mUdfpsController = null;
+ }
}
@Override
@@ -110,21 +154,28 @@
mPendingScreenState = screenState;
// Delay screen state transitions even longer while animations are running.
- boolean shouldDelayTransition = newState == DOZE_AOD
+ boolean shouldDelayTransitionEnteringDoze = newState == DOZE_AOD
&& mParameters.shouldControlScreenOff() && !turningOn;
- if (shouldDelayTransition) {
+ // Delay screen state transition longer if UDFPS is actively authenticating a fp
+ boolean shouldDelayTransitionForUDFPS = newState == DOZE_AOD
+ && mUdfpsController != null && mUdfpsController.isFingerDown();
+
+ if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
mWakeLock.setAcquired(true);
}
if (!messagePending) {
if (DEBUG) {
Log.d(TAG, "Display state changed to " + screenState + " delayed by "
- + (shouldDelayTransition ? ENTER_DOZE_DELAY : 1));
+ + (shouldDelayTransitionEnteringDoze ? ENTER_DOZE_DELAY : 1));
}
- if (shouldDelayTransition) {
+ if (shouldDelayTransitionEnteringDoze) {
mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
+ } else if (shouldDelayTransitionForUDFPS) {
+ mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
+ mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY);
} else {
mHandler.post(mApplyPendingScreenState);
}
@@ -139,6 +190,12 @@
}
private void applyPendingScreenState() {
+ if (mUdfpsController != null && mUdfpsController.isFingerDown()) {
+ mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
+ mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY);
+ return;
+ }
+
applyScreenState(mPendingScreenState);
mPendingScreenState = Display.STATE_UNKNOWN;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 455f3c0..756adca 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -92,6 +93,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final DelayableExecutor mMainExecutor;
+ private final KeyguardStateController mKeyguardStateController;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -179,7 +181,8 @@
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
SecureSettings secureSettings, AuthController authController,
@Main DelayableExecutor mainExecutor,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -198,6 +201,7 @@
mAuthController = authController;
mMainExecutor = mainExecutor;
mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
}
@Override
@@ -294,6 +298,7 @@
proximityCheckThenCall((result) -> {
if (result != null && result) {
// In pocket, drop event.
+ mDozeLog.traceSensorEventDropped(pulseReason, "prox reporting near");
return;
}
if (isDoubleTap || isTap) {
@@ -302,6 +307,10 @@
}
gentleWakeUp(pulseReason);
} else if (isPickup) {
+ if (shouldDropPickupEvent()) {
+ mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
+ return;
+ }
gentleWakeUp(pulseReason);
} else if (isUdfpsLongPress) {
final State state = mMachine.getState();
@@ -320,7 +329,7 @@
}, true /* alreadyPerformedProxCheck */, pulseReason);
}
- if (isPickup) {
+ if (isPickup && !shouldDropPickupEvent()) {
final long timeSinceNotification =
SystemClock.elapsedRealtime() - mNotificationPulseTime;
final boolean withinVibrationThreshold =
@@ -329,6 +338,10 @@
}
}
+ private boolean shouldDropPickupEvent() {
+ return mKeyguardStateController.isOccluded();
+ }
+
private void gentleWakeUp(int reason) {
// Log screen wake up reason (lift/pickup, tap, double-tap)
Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2817718..a104e97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2565,9 +2565,6 @@
private void handleNotifyScreenTurnedOn() {
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(LatencyTracker.ACTION_TURN_ON_SCREEN);
- }
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
mKeyguardViewControllerLazy.get().onScreenTurnedOn();
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
new file mode 100644
index 0000000..c8afd72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}-related messages.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface CollapsedSbFragmentLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 19193f9..84c5a57 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -100,6 +100,17 @@
return factory.create("PrivacyLog", 100);
}
+ /**
+ * Provides a logging buffer for
+ * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}.
+ */
+ @Provides
+ @SysUISingleton
+ @CollapsedSbFragmentLog
+ public static LogBuffer provideCollapsedSbFragmentLogBuffer(LogBufferFactory factory) {
+ return factory.create("CollapsedSbFragmentLog", 20);
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3167070..ecc3245 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -30,6 +30,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
@@ -46,6 +47,7 @@
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import javax.inject.Inject;
@@ -126,6 +128,8 @@
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
+ .setFlag(SYSUI_STATE_SCREEN_PINNING,
+ ActivityManagerWrapper.getInstance().isScreenPinningActive())
.commitUpdate(mDisplayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index a0cf44c..16b971f 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -102,6 +102,7 @@
}
@Provides
+ @Singleton
static PluginManager providesPluginManager(
Context context,
PluginActionManager.Factory instanceManagerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index a626681..d0aa710 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -29,6 +29,7 @@
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.appops.AppOpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -67,6 +68,7 @@
private val privacyLogger: PrivacyLogger,
private val keyguardStateController: KeyguardStateController,
private val appOpsController: AppOpsController,
+ private val uiEventLogger: UiEventLogger,
@VisibleForTesting private val dialogProvider: DialogProvider
) {
@@ -81,7 +83,8 @@
@Main uiExecutor: Executor,
privacyLogger: PrivacyLogger,
keyguardStateController: KeyguardStateController,
- appOpsController: AppOpsController
+ appOpsController: AppOpsController,
+ uiEventLogger: UiEventLogger
) : this(
permissionManager,
packageManager,
@@ -93,6 +96,7 @@
privacyLogger,
keyguardStateController,
appOpsController,
+ uiEventLogger,
defaultDialogProvider
)
@@ -105,6 +109,7 @@
private val onDialogDismissed = object : PrivacyDialog.OnDialogDismissed {
override fun onDialogDismissed() {
privacyLogger.logPrivacyDialogDismissed()
+ uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
dialog = null
}
}
@@ -114,6 +119,8 @@
val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ userId, packageName)
privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
if (!keyguardStateController.isUnlocked) {
// If we are locked, hide the dialog so the user can unlock
@@ -155,7 +162,7 @@
val items = usage.mapNotNull {
val type = filterType(permGroupToPrivacyType(it.permGroupName))
val userInfo = userInfos.firstOrNull { ui -> ui.id == UserHandle.getUserId(it.uid) }
- userInfo?.let { ui ->
+ if (userInfo != null || it.isPhoneCall) {
type?.let { t ->
// Only try to get the app name if we actually need it
val appName = if (it.isPhoneCall) {
@@ -171,10 +178,14 @@
it.attribution,
it.lastAccess,
it.isActive,
- ui.isManagedProfile,
+ // If there's no user info, we're in a phoneCall in secondary user
+ userInfo?.isManagedProfile ?: false,
it.isPhoneCall
)
}
+ } else {
+ // No matching user or phone call
+ null
}
}
uiExecutor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
new file mode 100644
index 0000000..3ecc5a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyDialogEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Privacy dialog is clicked by user to go to the app settings page.")
+ PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS(904),
+
+ @UiEvent(doc = "Privacy dialog is dismissed by user.")
+ PRIVACY_DIALOG_DISMISSED(905);
+
+ override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index fcfa72a..f6c89a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -36,7 +36,7 @@
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userInfoController: UserInfoController,
- private val multiUserSwitchController: MultiUserSwitchController,
+ private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val deviceProvisionedController: DeviceProvisionedController,
private val falsingManager: FalsingManager,
private val metricsLogger: MetricsLogger,
@@ -60,8 +60,8 @@
fun build(): FooterActionsController {
return FooterActionsController(view, qsPanelController, activityStarter, userManager,
- userInfoController, multiUserSwitchController, deviceProvisionedController,
- falsingManager, metricsLogger, tunerService, globalActionsDialog, uiEventLogger,
- showPMLiteButton, buttonsVisibleState)
+ userInfoController, multiUserSwitchControllerFactory.create(view),
+ deviceProvisionedController, falsingManager, metricsLogger, tunerService,
+ globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 6e09f22..9025427 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -42,6 +42,7 @@
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSlider;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
@@ -60,6 +61,7 @@
private final QSCustomizerController mQsCustomizerController;
private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final FalsingManager mFalsingManager;
+ private final CommandQueue mCommandQueue;
private final BrightnessController mBrightnessController;
private final BrightnessSlider mBrightnessSlider;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
@@ -97,7 +99,8 @@
QSTileRevealController.Factory qsTileRevealControllerFactory,
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
- BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager) {
+ BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager,
+ CommandQueue commandQueue) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQsSecurityFooter = qsSecurityFooter;
@@ -105,6 +108,7 @@
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
+ mCommandQueue = commandQueue;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSlider = brightnessSliderFactory.create(getContext(), mView);
@@ -274,6 +278,14 @@
/** */
public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
+ // TODO(b/199296365)
+ // Workaround for opening detail from QQS, when there might not be enough space to
+ // display e.g. in case of multiuser detail from split shade. Currently showing detail works
+ // only for QS (mView below) and that's why expanding panel (thus showing QS instead of QQS)
+ // makes it displayed correctly.
+ if (!isExpanded()) {
+ mCommandQueue.animateExpandSettingsPanel(null);
+ }
mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index eff34d8..0196769 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -39,6 +39,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
@@ -96,6 +97,7 @@
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
+ private final FeatureFlags mFeatureFlags;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -126,7 +128,8 @@
UserTracker userTracker,
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
+ FeatureFlags featureFlags
) {
mIconController = iconController;
mContext = context;
@@ -149,6 +152,7 @@
mUserTracker = userTracker;
mSecureSettings = secureSettings;
mCustomTileStatePersister = customTileStatePersister;
+ mFeatureFlags = featureFlags;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -272,7 +276,7 @@
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
- final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
+ final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags);
int currentUser = mUserTracker.getUserId();
if (currentUser != mCurrentUser) {
mUserContext = mUserTracker.getUserContext();
@@ -341,7 +345,7 @@
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
- changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
+ changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
@@ -409,7 +413,7 @@
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
- final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+ final List<String> tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags);
if (changeFunction.test(tileSpecs)) {
saveTilesToSettings(tileSpecs);
}
@@ -498,7 +502,8 @@
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
- protected static List<String> loadTileSpecs(Context context, String tileList) {
+ protected static List<String> loadTileSpecs(
+ Context context, String tileList, FeatureFlags featureFlags) {
final Resources res = context.getResources();
if (TextUtils.isEmpty(tileList)) {
@@ -531,6 +536,21 @@
}
}
}
+ if (featureFlags.isProviderModelSettingEnabled()) {
+ if (!tiles.contains("internet")) {
+ if (tiles.contains("wifi")) {
+ // Replace the WiFi with Internet, and remove the Cell
+ tiles.set(tiles.indexOf("wifi"), "internet");
+ tiles.remove("cell");
+ } else if (tiles.contains("cell")) {
+ // Replace the Cell with Internet
+ tiles.set(tiles.indexOf("cell"), "internet");
+ }
+ } else {
+ tiles.remove("wifi");
+ tiles.remove("cell");
+ }
+ }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 3c2f35b..993bbd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -34,6 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.QSTileHost;
@@ -62,6 +63,7 @@
private final Executor mBgExecutor;
private final Context mContext;
private final UserTracker mUserTracker;
+ private final FeatureFlags mFeatureFlags;
private TileStateListener mListener;
private boolean mFinished;
@@ -71,12 +73,14 @@
Context context,
UserTracker userTracker,
@Main Executor mainExecutor,
- @Background Executor bgExecutor
+ @Background Executor bgExecutor,
+ FeatureFlags featureFlags
) {
mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mUserTracker = userTracker;
+ mFeatureFlags = featureFlags;
}
public void setListener(TileStateListener listener) {
@@ -117,6 +121,10 @@
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
+ if (mFeatureFlags.isProviderModelSettingEnabled()) {
+ possibleTiles.remove("cell");
+ possibleTiles.remove("wifi");
+ }
for (String spec : possibleTiles) {
// Only add current and stock tiles that can be created from QSFactoryImpl.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 386769c..b11420a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -39,7 +39,6 @@
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.statusbar.phone.MultiUserSwitch;
import javax.inject.Named;
@@ -82,12 +81,6 @@
/** */
@Provides
- static MultiUserSwitch providesMultiUserSWitch(QSFooterView qsFooterView) {
- return qsFooterView.findViewById(R.id.multi_user_switch);
- }
-
- /** */
- @Provides
static QSPanel provideQSPanel(@RootView View view) {
return view.findViewById(R.id.quick_settings_panel);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 8e886e8..b1af841 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -275,7 +275,7 @@
return;
}
mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
- mInfo.dataContentDescription = indicators.description != null
+ mInfo.dataContentDescription = indicators.qsDescription != null
? indicators.typeContentDescriptionHtml : null;
mInfo.activityIn = indicators.activityIn;
mInfo.activityOut = indicators.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index cc9e748..530804e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -279,9 +279,9 @@
// Not data sim, don't display.
return;
}
- mCellularInfo.mDataSubscriptionName = indicators.description == null
- ? mController.getMobileDataNetworkName() : indicators.description;
- mCellularInfo.mDataContentDescription = indicators.description != null
+ mCellularInfo.mDataSubscriptionName = indicators.qsDescription == null
+ ? mController.getMobileDataNetworkName() : indicators.qsDescription;
+ mCellularInfo.mDataContentDescription = indicators.qsDescription != null
? indicators.typeContentDescriptionHtml : null;
mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon;
mCellularInfo.mQsTypeIcon = indicators.qsType;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index b1db8a9..08da680 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -52,6 +52,7 @@
import android.widget.Switch;
import android.widget.TextView;
+import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
@@ -60,7 +61,6 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.Utils;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -105,12 +105,10 @@
private View mDivider;
private ProgressBar mProgressBar;
private LinearLayout mInternetDialogLayout;
- private LinearLayout mInternetListLayout;
private LinearLayout mConnectedWifListLayout;
- private LinearLayout mConnectedWifList;
private LinearLayout mMobileNetworkLayout;
- private LinearLayout mMobileNetworkList;
private LinearLayout mTurnWifiOnLayout;
+ private LinearLayout mEthernetLayout;
private TextView mWifiToggleTitleText;
private LinearLayout mSeeAllLayout;
private RecyclerView mWifiRecyclerView;
@@ -126,7 +124,6 @@
private Button mDoneButton;
private Drawable mBackgroundOn;
private int mListMaxHeight;
- private int mLayoutWidth;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData;
@@ -181,8 +178,6 @@
};
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.internet_dialog_list_max_height);
- mLayoutWidth = context.getResources().getDimensionPixelSize(
- R.dimen.internet_dialog_list_max_width);
mUiEventLogger = uiEventLogger;
mAdapter = new InternetAdapter(mInternetDialogController);
if (!aboveStatusBar) {
@@ -221,13 +216,11 @@
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mDivider = mDialogView.requireViewById(R.id.divider);
mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
- mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+ mEthernetLayout = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
- mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
- mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
@@ -309,6 +302,7 @@
} else {
mInternetDialogSubTitle.setText(getSubtitleText());
}
+ updateEthernet();
setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
if (!mCanConfigWifi) {
@@ -355,6 +349,12 @@
mDoneButton.setOnClickListener(v -> dismiss());
}
+ @MainThread
+ private void updateEthernet() {
+ mEthernetLayout.setVisibility(
+ mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
+ }
+
private void setMobileDataLayout(boolean isCellularNetwork) {
if (mInternetDialogController.isAirplaneModeEnabled()
|| !mInternetDialogController.hasCarrier()) {
@@ -371,38 +371,33 @@
mMobileSummaryText.setVisibility(View.GONE);
}
mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
- if (mInternetDialogController.isNightMode()) {
- int titleColor = isCellularNetwork ? mContext.getColor(
- R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorPrimary);
- int summaryColor = isCellularNetwork ? mContext.getColor(
- R.color.connected_network_secondary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorSecondary);
-
- mMobileTitleText.setTextColor(titleColor);
- mMobileSummaryText.setTextColor(summaryColor);
- }
+ mMobileTitleText.setTextAppearance(isCellularNetwork
+ ? R.style.TextAppearance_InternetDialog_Active
+ : R.style.TextAppearance_InternetDialog);
+ mMobileSummaryText.setTextAppearance(isCellularNetwork
+ ? R.style.TextAppearance_InternetDialog_Secondary_Active
+ : R.style.TextAppearance_InternetDialog_Secondary);
mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
+ @MainThread
private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
mWiFiToggle.setChecked(isWifiEnabled);
- if (isDeviceLocked && mInternetDialogController.isNightMode()) {
- int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
- R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
- mContext, android.R.attr.textColorPrimary);
- mWifiToggleTitleText.setTextColor(titleColor);
+ if (isDeviceLocked) {
+ mWifiToggleTitleText.setTextAppearance((mConnectedWifiEntry != null)
+ ? R.style.TextAppearance_InternetDialog_Active
+ : R.style.TextAppearance_InternetDialog);
}
mTurnWifiOnLayout.setBackground(
(isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
}
+ @MainThread
private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
- mConnectedWifListLayout.setBackground(null);
mConnectedWifListLayout.setVisibility(View.GONE);
return;
}
@@ -411,15 +406,8 @@
mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
mConnectedWifiIcon.setImageDrawable(
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
- if (mInternetDialogController.isNightMode()) {
- mConnectedWifiTitleText.setTextColor(
- mContext.getColor(R.color.connected_network_primary_color));
- mConnectedWifiSummaryText.setTextColor(
- mContext.getColor(R.color.connected_network_secondary_color));
- }
mWifiSettingsIcon.setColorFilter(
mContext.getColor(R.color.connected_network_primary_color));
- mConnectedWifListLayout.setBackground(mBackgroundOn);
}
void onClickConnectedWifi() {
@@ -528,11 +516,18 @@
}
@Override
+ @WorkerThread
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
mHandler.post(() -> updateDialog());
}
@Override
+ @WorkerThread
+ public void onLost(Network network) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
public void onSubscriptionsChanged(int defaultDataSubId) {
mDefaultDataSubId = defaultDataSubId;
mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 70f52ad..95d3915 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -31,7 +30,6 @@
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
@@ -49,6 +47,7 @@
import android.view.Gravity;
import android.widget.Toast;
+import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -58,9 +57,11 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.SignalIcon;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.systemui.R;
@@ -131,6 +132,7 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
@VisibleForTesting
protected ActivityStarter mActivityStarter;
@@ -144,6 +146,8 @@
protected boolean mCanConfigWifi;
@VisibleForTesting
protected KeyguardStateController mKeyguardStateController;
+ @VisibleForTesting
+ protected boolean mHasEthernet = false;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -191,6 +195,7 @@
mAccessPointController = accessPointController;
mConfig = MobileMappings.Config.readConfig(mContext);
mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
+ mConnectivityManagerNetworkCallback = new DataConnectivityListener();
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -214,9 +219,7 @@
mInternetTelephonyCallback = new InternetTelephonyCallback();
mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
// Listen the connectivity changes
- mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), new DataConnectivityListener(), mHandler);
+ mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
mCanConfigWifi = canConfigWifi;
scanWifiAccessPoints();
}
@@ -231,6 +234,7 @@
mOnSubscriptionsChangedListener);
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
}
@VisibleForTesting
@@ -349,11 +353,6 @@
return drawable;
}
- boolean isNightMode() {
- return (mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- }
-
Drawable getSignalStrengthDrawable() {
Drawable drawable = mContext.getDrawable(
R.drawable.ic_signal_strength_zero_bar_no_internet);
@@ -534,6 +533,14 @@
}
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ if (mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork()) {
+ SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
+ TelephonyIcons.CARRIER_MERGED_WIFI;
+ resId = carrierMergedWifiIconGroup.dataContentDescription;
+ }
+
return resId != 0
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
@@ -790,6 +797,9 @@
}
int count = MAX_WIFI_ENTRY_COUNT;
+ if (mHasEthernet) {
+ count -= 1;
+ }
if (hasCarrier()) {
count -= 1;
}
@@ -860,21 +870,30 @@
@Override
@WorkerThread
public void onCapabilitiesChanged(@NonNull Network network,
- @NonNull NetworkCapabilities networkCapabilities) {
- if (mCanConfigWifi) {
- for (int transport : networkCapabilities.getTransportTypes()) {
- if (transport == NetworkCapabilities.TRANSPORT_WIFI) {
- scanWifiAccessPoints();
- break;
- }
- }
+ @NonNull NetworkCapabilities capabilities) {
+ mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
+ if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_WIFI))) {
+ scanWifiAccessPoints();
}
- final Network activeNetwork = mConnectivityManager.getActiveNetwork();
- if (activeNetwork != null && activeNetwork.equals(network)) {
- // update UI
- mCallback.onCapabilitiesChanged(network, networkCapabilities);
- }
+ // update UI
+ mCallback.onCapabilitiesChanged(network, capabilities);
}
+
+ @Override
+ @WorkerThread
+ public void onLost(@NonNull Network network) {
+ mHasEthernet = false;
+ mCallback.onLost(network);
+ }
+ }
+
+ /**
+ * Return {@code true} If the Ethernet exists
+ */
+ @MainThread
+ public boolean hasEthernet() {
+ return mHasEthernet;
}
private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
@@ -924,6 +943,8 @@
void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+ void onLost(@NonNull Network network);
+
void onSubscriptionsChanged(int defaultDataSubId);
void onServiceStateChanged(ServiceState serviceState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
new file mode 100644
index 0000000..cf34db2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar
+
+import android.app.StatusBarManager.DISABLE_BACK
+import android.app.StatusBarManager.DISABLE_CLOCK
+import android.app.StatusBarManager.DISABLE_EXPAND
+import android.app.StatusBarManager.DISABLE_HOME
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP
+import android.app.StatusBarManager.DISABLE_RECENT
+import android.app.StatusBarManager.DISABLE_SEARCH
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS
+import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A singleton that creates concise but readable strings representing the values of the disable
+ * flags for debugging.
+ *
+ * See [CommandQueue.disable] for information about disable flags.
+ *
+ * Note that, for both lists passed in, each flag must have a distinct [DisableFlag.flagIsSetSymbol]
+ * and distinct [DisableFlag.flagNotSetSymbol] within the list. If this isn't true, the logs could
+ * be ambiguous so an [IllegalArgumentException] is thrown.
+ */
+@SysUISingleton
+class DisableFlagsLogger constructor(
+ private val disable1FlagsList: List<DisableFlag>,
+ private val disable2FlagsList: List<DisableFlag>
+) {
+
+ @Inject
+ constructor() : this(defaultDisable1FlagsList, defaultDisable2FlagsList)
+
+ init {
+ if (flagsListHasDuplicateSymbols(disable1FlagsList)) {
+ throw IllegalArgumentException("disable1 flags must have unique symbols")
+ }
+ if (flagsListHasDuplicateSymbols(disable2FlagsList)) {
+ throw IllegalArgumentException("disable2 flags must have unique symbols")
+ }
+ }
+
+ private fun flagsListHasDuplicateSymbols(list: List<DisableFlag>): Boolean {
+ val numDistinctFlagOffStatus = list.map { it.getFlagStatus(0) }.distinct().count()
+ val numDistinctFlagOnStatus = list
+ .map { it.getFlagStatus(Int.MAX_VALUE) }
+ .distinct()
+ .count()
+ return numDistinctFlagOffStatus < list.count() || numDistinctFlagOnStatus < list.count()
+ }
+
+ /**
+ * Returns a string representing the, old, new, and new-after-modification disable flag states,
+ * as well as the differences between each of the states.
+ *
+ * Example:
+ * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
+ * EnaihBcRso.qInGR (.n)
+ *
+ * A capital character signifies the flag is set and a lowercase character signifies that the
+ * flag isn't set. The flag states will be logged in the same order as the passed-in lists.
+ *
+ * The difference between states is written between parentheses, and won't be included if there
+ * is no difference. the new-after-modification state also won't be included if there's no
+ * difference from the new state.
+ *
+ * @param old the disable state that had been previously sent.
+ * @param new the new disable state that has just been sent.
+ * @param newAfterLocalModification the new disable states after a class has locally modified
+ * them. Null if the class does not locally modify.
+ */
+ fun getDisableFlagsString(
+ old: DisableState,
+ new: DisableState,
+ newAfterLocalModification: DisableState? = null
+ ): String {
+ val builder = StringBuilder("Received new disable state. ")
+ builder.append("Old: ")
+ builder.append(getFlagsString(old))
+ builder.append(" | New: ")
+ if (old != new) {
+ builder.append(getFlagsStringWithDiff(old, new))
+ } else {
+ builder.append(getFlagsString(old))
+ }
+
+ if (newAfterLocalModification != null && new != newAfterLocalModification) {
+ builder.append(" | New after local modification: ")
+ builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
+ }
+
+ return builder.toString()
+ }
+
+ /**
+ * Returns a string representing [new] state, as well as the difference from [old] to [new]
+ * (if there is one).
+ */
+ private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
+ val builder = StringBuilder()
+ builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ return builder.toString()
+ }
+
+ /**
+ * Returns a string representing the difference between [old] and [new], or an empty string if
+ * there is no difference.
+ *
+ * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
+ * "(BC.e)".
+ */
+ private fun getDiffString(old: DisableState, new: DisableState): String {
+ if (old == new) {
+ return ""
+ }
+
+ val builder = StringBuilder("(")
+ disable1FlagsList.forEach {
+ val newSymbol = it.getFlagStatus(new.disable1)
+ if (it.getFlagStatus(old.disable1) != newSymbol) {
+ builder.append(newSymbol)
+ }
+ }
+ builder.append(".")
+ disable2FlagsList.forEach {
+ val newSymbol = it.getFlagStatus(new.disable2)
+ if (it.getFlagStatus(old.disable2) != newSymbol) {
+ builder.append(newSymbol)
+ }
+ }
+ builder.append(")")
+ return builder.toString()
+ }
+
+ /** Returns a string representing the disable flag states, e.g. "EnaihBcRso.qiNGR". */
+ private fun getFlagsString(state: DisableState): String {
+ val builder = StringBuilder("")
+ disable1FlagsList.forEach { builder.append(it.getFlagStatus(state.disable1)) }
+ builder.append(".")
+ disable2FlagsList.forEach { builder.append(it.getFlagStatus(state.disable2)) }
+ return builder.toString()
+ }
+
+ /** A POJO representing each disable flag. */
+ class DisableFlag(
+ private val bitMask: Int,
+ private val flagIsSetSymbol: Char,
+ private val flagNotSetSymbol: Char
+ ) {
+
+ /**
+ * Returns a character representing whether or not this flag is set in [state].
+ *
+ * A capital character signifies the flag is set and a lowercase character signifies that
+ * the flag isn't set.
+ */
+ internal fun getFlagStatus(state: Int): Char =
+ if (0 != state and this.bitMask) this.flagIsSetSymbol
+ else this.flagNotSetSymbol
+ }
+
+ /** POJO to hold [disable1] and [disable2]. */
+ data class DisableState(val disable1: Int, val disable2: Int)
+}
+
+// LINT.IfChange
+private val defaultDisable1FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
+ DisableFlagsLogger.DisableFlag(DISABLE_EXPAND, 'E', 'e'),
+ DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ICONS, 'N', 'n'),
+ DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ALERTS, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(DISABLE_SYSTEM_INFO, 'I', 'i'),
+ DisableFlagsLogger.DisableFlag(DISABLE_HOME, 'H', 'h'),
+ DisableFlagsLogger.DisableFlag(DISABLE_BACK, 'B', 'b'),
+ DisableFlagsLogger.DisableFlag(DISABLE_CLOCK, 'C', 'c'),
+ DisableFlagsLogger.DisableFlag(DISABLE_RECENT, 'R', 'r'),
+ DisableFlagsLogger.DisableFlag(DISABLE_SEARCH, 'S', 's'),
+ DisableFlagsLogger.DisableFlag(DISABLE_ONGOING_CALL_CHIP, 'O', 'o')
+)
+
+private val defaultDisable2FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
+ DisableFlagsLogger.DisableFlag(DISABLE2_QUICK_SETTINGS, 'Q', 'q'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_SYSTEM_ICONS, 'I', 'i'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_NOTIFICATION_SHADE, 'N', 'n'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r')
+)
+// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 445715e..17bcfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -66,6 +66,7 @@
import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -455,7 +456,8 @@
new KeyguardIndication.Builder()
.setMessage(mContext.getResources().getString(
com.android.internal.R.string.global_action_logout))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(Utils.getColorAttr(
+ mContext, com.android.internal.R.attr.textColorOnAccent))
.setBackground(mContext.getDrawable(
com.android.systemui.R.drawable.logout_button_background))
.setClickListener((view) -> {
@@ -798,7 +800,9 @@
* Show message on the keyguard for how the user can unlock/enter their device.
*/
public void showActionToUnlock() {
- if (mDozing) {
+ if (mDozing
+ && !mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ KeyguardUpdateMonitor.getCurrentUser())) {
return;
}
@@ -809,7 +813,7 @@
String message = mContext.getString(R.string.keyguard_retry);
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
- } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+ } else {
showTransientIndication(mContext.getString(R.string.keyguard_unlock),
false /* isError */, true /* hideOnScreenOff */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index f0eb084..efec94f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -48,7 +48,9 @@
val sectioner = object : NotifSectioner("People") {
override fun isInSection(entry: ListEntry): Boolean =
isConversation(entry.representativeEntry!!)
- override fun getHeaderNodeController() = peopleHeaderController
+ override fun getHeaderNodeController() =
+ // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
+ if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
}
override fun attach(pipeline: NotifPipeline) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index 6e98c27..1bde312 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -205,7 +205,11 @@
@Nullable
@Override
public NodeController getHeaderNodeController() {
- return mIncomingHeaderController;
+ // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
+ if (RankingCoordinator.SHOW_ALL_SECTIONS) {
+ return mIncomingHeaderController;
+ }
+ return null;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 6da4d8b..d556e97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -41,6 +41,7 @@
*/
@SysUISingleton
public class RankingCoordinator implements Coordinator {
+ public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
private final NodeController mSilentHeaderController;
@@ -83,7 +84,11 @@
@Nullable
@Override
public NodeController getHeaderNodeController() {
- return mAlertingHeaderController;
+ // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController
+ if (SHOW_ALL_SECTIONS) {
+ return mAlertingHeaderController;
+ }
+ return null;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0d8e850..e956046 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -3201,13 +3201,6 @@
}
}
- /** Sets whether dismiss gestures are right-to-left (instead of left-to-right). */
- public void setDismissRtl(boolean dismissRtl) {
- if (mMenuRow != null) {
- mMenuRow.setDismissRtl(dismissRtl);
- }
- }
-
private static class NotificationViewState extends ExpandableViewState {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c3dc700..b7d721e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -82,7 +82,6 @@
private ArrayList<MenuItem> mRightMenuItems;
private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
- private boolean mDismissRtl;
private ValueAnimator mFadeAnimator;
private boolean mAnimating;
@@ -787,14 +786,6 @@
return getParent().canViewBeDismissed();
}
- @Override
- public void setDismissRtl(boolean dismissRtl) {
- mDismissRtl = dismissRtl;
- if (mMenuContainer != null) {
- createMenuViews(true);
- }
- }
-
public static class NotificationMenuItem implements MenuItem {
View mMenuView;
GutsContent mGutsContent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3d72ae7..2ecf8e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -163,7 +163,6 @@
private final Paint mBackgroundPaint = new Paint();
private final boolean mShouldDrawNotificationBackground;
private boolean mHighPriorityBeforeSpeedBump;
- private boolean mDismissRtl;
private float mExpandedHeight;
private int mOwnScrollY;
@@ -620,16 +619,6 @@
addView(mFgsSectionView, -1);
}
- void updateDismissRtlSetting(boolean dismissRtl) {
- mDismissRtl = dismissRtl;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) child).setDismissRtl(dismissRtl);
- }
- }
- }
-
/**
* Set the overexpansion of the panel to be applied to the view.
*/
@@ -2918,7 +2907,6 @@
updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- row.setDismissRtl(mDismissRtl);
row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f1ae3da..f2da28c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -762,9 +762,6 @@
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {
- case Settings.Secure.NOTIFICATION_DISMISS_RTL:
- mView.updateDismissRtlSetting("1".equals(newValue));
- break;
case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
updateFooter();
break;
@@ -774,7 +771,6 @@
}
},
HIGH_PRIORITY,
- Settings.Secure.NOTIFICATION_DISMISS_RTL,
Settings.Secure.NOTIFICATION_HISTORY_ENABLED);
mKeyguardMediaController.setVisibilityChangedListener(visible -> {
@@ -1272,6 +1268,9 @@
}
public void updateSectionBoundaries(String reason) {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ return;
+ }
mView.updateSectionBoundaries(reason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 6e201048..2c76cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -116,7 +116,7 @@
/**
* Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the
- * device while being requested when keyguard is occluded.
+ * device while being requested when keyguard is occluded or showing.
*/
public static final int MODE_UNLOCK_COLLAPSING = 5;
@@ -425,6 +425,11 @@
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE,
+ true /* force */,
+ false /* delayed */,
+ BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
mPendingShowBouncer = false;
mKeyguardViewController.notifyKeyguardAuthenticated(
false /* strongAuth */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 45348d9..1bac8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -42,6 +42,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
@@ -93,6 +94,7 @@
private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
private final CommandQueue mCommandQueue;
+ private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -131,6 +133,7 @@
StatusBarStateController statusBarStateController,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory
) {
mOngoingCallController = ongoingCallController;
@@ -144,6 +147,7 @@
mStatusBarStateController = statusBarStateController;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mCommandQueue = commandQueue;
+ mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
}
@@ -244,7 +248,14 @@
if (displayId != getContext().getDisplayId()) {
return;
}
+
+ int state1BeforeAdjustment = state1;
state1 = adjustDisableFlags(state1);
+
+ mCollapsedStatusBarFragmentLogger.logDisableFlagChange(
+ new DisableState(state1BeforeAdjustment, state2),
+ new DisableState(state1, state2));
+
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
final int old2 = mDisabled2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
new file mode 100644
index 0000000..3c2b555
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.CollapsedSbFragmentLog
+import com.android.systemui.statusbar.DisableFlagsLogger
+import javax.inject.Inject
+
+/** Used by [CollapsedStatusBarFragment] to log messages to a [LogBuffer]. */
+class CollapsedStatusBarFragmentLogger @Inject constructor(
+ @CollapsedSbFragmentLog private val buffer: LogBuffer,
+ private val disableFlagsLogger: DisableFlagsLogger,
+) {
+
+ /** Logs a string representing the old and new disable flag states to [buffer]. */
+ fun logDisableFlagChange(
+ oldState: DisableFlagsLogger.DisableState,
+ newState: DisableFlagsLogger.DisableState) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = oldState.disable1
+ int2 = oldState.disable2
+ long1 = newState.disable1.toLong()
+ long2 = newState.disable2.toLong()
+ },
+ {
+ disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(int1, int2),
+ DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt())
+ )
+ }
+ )
+ }
+}
+
+private const val TAG = "CollapsedSbFragment"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index ab7f9d3..a8c62fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -270,13 +270,6 @@
}
/**
- * Whether the brightness sensor uses the proximity sensor.
- */
- public boolean brightnessUsesProx() {
- return mResources.getBoolean(R.bool.doze_brightness_uses_prox);
- }
-
- /**
* Callback to listen for DozeParameter changes.
*/
public void addCallback(Callback callback) {
@@ -300,6 +293,7 @@
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn());
pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
pw.print("getPulseDuration(): "); pw.println(getPulseDuration());
pw.print("getPulseInDuration(): "); pw.println(getPulseInDuration());
@@ -312,7 +306,6 @@
pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
pw.println(getSelectivelyRegisterSensorsUsingProx());
- pw.print("brightnessUsesProx(): "); pw.println(brightnessUsesProx());
}
interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 8c0dfc5..f1d5e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -252,8 +252,6 @@
mKeyguardViewController.resetSecurityContainer();
showPromptReason(mBouncerPromptReason);
}
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
- SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index f1cde8a..a5b5f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -28,7 +28,10 @@
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.StyleRes;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -39,6 +42,12 @@
*/
public class KeyguardIndicationTextView extends TextView {
private static final long MSG_MIN_DURATION_MILLIS_DEFAULT = 1500;
+
+ @StyleRes
+ private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
+ @StyleRes
+ private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
+
private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
private LinkedList<CharSequence> mMessages = new LinkedList<>();
@@ -136,6 +145,14 @@
public void onAnimationEnd(Animator animator) {
KeyguardIndication info = mKeyguardIndicationInfo.poll();
if (info != null) {
+ // First, update the style.
+ // If a background is set on the text, we don't want shadow on the text
+ if (info.getBackground() != null) {
+ setTextAppearance(sButtonStyleId);
+ } else {
+ setTextAppearance(sStyleId);
+ }
+ setBackground(info.getBackground());
setTextColor(info.getTextColor());
setOnClickListener(info.getClickListener());
setClickable(info.getClickListener() != null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index f4cc146..5f44a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -119,6 +119,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ loadDimens();
MarginLayoutParams lp = (MarginLayoutParams) mMultiUserAvatar.getLayoutParams();
lp.width = lp.height = getResources().getDimensionPixelSize(
@@ -128,6 +129,22 @@
// System icons
updateSystemIconsLayoutParams();
+ // mStatusIconArea
+ mStatusIconArea.setPaddingRelative(
+ mStatusIconArea.getPaddingStart(),
+ getResources().getDimensionPixelSize(R.dimen.status_bar_padding_top),
+ mStatusIconArea.getPaddingEnd(),
+ mStatusIconArea.getPaddingBottom()
+ );
+
+ // mStatusIconContainer
+ mStatusIconContainer.setPaddingRelative(
+ mStatusIconContainer.getPaddingStart(),
+ mStatusIconContainer.getPaddingTop(),
+ getResources().getDimensionPixelSize(R.dimen.signal_cluster_battery_padding),
+ mStatusIconContainer.getPaddingBottom()
+ );
+
// Respect font size setting.
mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 51eb496..abee7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -225,19 +225,19 @@
}
}
+ // If no one is light, all icons become white.
+ if (numLightStacks == 0) {
+ mStatusBarIconController.getTransitionsController().setIconsDark(
+ false, animateChange());
+ }
+
// If all stacks are light, all icons get dark.
- if (numLightStacks == numStacks) {
+ else if (numLightStacks == numStacks) {
mStatusBarIconController.setIconsDarkArea(null);
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
}
- // If no one is light, all icons become white.
- else if (numLightStacks == 0) {
- mStatusBarIconController.getTransitionsController().setIconsDark(
- false, animateChange());
- }
-
// Not the same for every stack, magic!
else {
mStatusBarIconController.setIconsDarkArea(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 2cacf92..d69b31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -26,6 +26,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.user.UserSwitchDialogController;
@@ -35,7 +36,6 @@
import javax.inject.Inject;
/** View Controller for {@link MultiUserSwitch}. */
-@QSScope
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
private final UserManager mUserManager;
private final UserSwitcherController mUserSwitcherController;
@@ -68,8 +68,35 @@
}
};
- @Inject
- public MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
+ @QSScope
+ public static class Factory {
+ private final UserManager mUserManager;
+ private final UserSwitcherController mUserSwitcherController;
+ private final QSDetailDisplayer mQsDetailDisplayer;
+ private final FalsingManager mFalsingManager;
+ private final UserSwitchDialogController mUserSwitchDialogController;
+ private final FeatureFlags mFeatureFlags;
+
+ @Inject
+ public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
+ QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager,
+ UserSwitchDialogController userSwitchDialogController, FeatureFlags featureFlags) {
+ mUserManager = userManager;
+ mUserSwitcherController = userSwitcherController;
+ mQsDetailDisplayer = qsDetailDisplayer;
+ mFalsingManager = falsingManager;
+ mUserSwitchDialogController = userSwitchDialogController;
+ mFeatureFlags = featureFlags;
+ }
+
+ public MultiUserSwitchController create(FooterActionsView view) {
+ return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
+ mUserManager, mUserSwitcherController, mQsDetailDisplayer,
+ mFalsingManager, mUserSwitchDialogController, mFeatureFlags);
+ }
+ }
+
+ private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
FeatureFlags featureFlags) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3aca18e..6d76e95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -883,13 +883,13 @@
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
mCommunalView = mView.findViewById(R.id.communal_host);
- UserAvatarView userAvatarView = null;
+ FrameLayout userAvatarContainer = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
if (mKeyguardQsUserSwitchEnabled) {
ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
- userAvatarView = (UserAvatarView) stub.inflate();
+ userAvatarContainer = (FrameLayout) stub.inflate();
} else {
ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
@@ -919,7 +919,7 @@
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
- userAvatarView,
+ userAvatarContainer,
keyguardUserSwitcherView,
mView.findViewById(R.id.idle_host_view),
mCommunalView);
@@ -1011,7 +1011,7 @@
}
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
- UserAvatarView userAvatarView,
+ FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView,
IdleHostView idleHostView,
CommunalHostView communalView) {
@@ -1174,7 +1174,7 @@
!mKeyguardQsUserSwitchEnabled
&& mKeyguardUserSwitcherEnabled
&& isUserSwitcherEnabled;
- UserAvatarView userAvatarView = (UserAvatarView) reInflateStub(
+ FrameLayout userAvatarView = (FrameLayout) reInflateStub(
R.id.keyguard_qs_user_switch_view /* viewId */,
R.id.keyguard_qs_user_switch_stub /* stubId */,
R.layout.keyguard_qs_user_switch /* layoutId */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index a09b30f..af556a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -79,6 +79,8 @@
private int mStatusBarHeight;
@Nullable
private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
+ @Nullable
+ private PanelExpansionStateChangedListener mPanelExpansionStateChangedListener;
private PanelEnabledProvider mPanelEnabledProvider;
@@ -102,6 +104,10 @@
mExpansionChangedListeners = listeners;
}
+ void setPanelExpansionStateChangedListener(PanelExpansionStateChangedListener listener) {
+ mPanelExpansionStateChangedListener = listener;
+ }
+
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
}
@@ -289,11 +295,10 @@
super.panelExpansionChanged(frac, expanded);
updateScrimFraction();
if ((frac == 0 || frac == 1)) {
- if (mBar.getNavigationBarView() != null) {
- mBar.getNavigationBarView().onStatusBarPanelStateChanged();
- }
- if (mBar.getNotificationPanelViewController() != null) {
- mBar.getNotificationPanelViewController().updateSystemUiStateFlags();
+ if (mPanelExpansionStateChangedListener != null) {
+ mPanelExpansionStateChangedListener.onPanelExpansionStateChanged();
+ } else {
+ Log.w(TAG, "No PanelExpansionStateChangedListener provided.");
}
}
@@ -412,4 +417,10 @@
/** Returns true if the panel is enabled and false otherwise. */
boolean panelEnabled();
}
+
+ /** A listener that will be notified when a panel's expansion state may have changed. */
+ public interface PanelExpansionStateChangedListener {
+ /** Called when a panel's expansion state may have changed. */
+ void onPanelExpansionStateChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
deleted file mode 100644
index b36b67d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.ViewController;
-
-/** Controller for {@link PhoneStatusBarView}. */
-public class PhoneStatusBarViewController extends ViewController<PhoneStatusBarView> {
-
- protected PhoneStatusBarViewController(
- PhoneStatusBarView view,
- CommandQueue commandQueue) {
- super(view);
- mView.setPanelEnabledProvider(commandQueue::panelsEnabled);
- }
-
- @Override
- protected void onViewAttached() {
- }
-
- @Override
- protected void onViewDetached() {
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
new file mode 100644
index 0000000..28040fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Point
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.ViewController
+
+/** Controller for [PhoneStatusBarView]. */
+class PhoneStatusBarViewController(
+ view: PhoneStatusBarView,
+ commandQueue: CommandQueue,
+ statusBarMoveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
+ panelExpansionStateChangedListener: PhoneStatusBarView.PanelExpansionStateChangedListener,
+) : ViewController<PhoneStatusBarView>(view) {
+
+ override fun onViewAttached() {}
+ override fun onViewDetached() {}
+
+ init {
+ mView.setPanelEnabledProvider {
+ commandQueue.panelsEnabled()
+ }
+ mView.setPanelExpansionStateChangedListener(panelExpansionStateChangedListener)
+
+ statusBarMoveFromCenterAnimationController?.let { animationController ->
+ val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
+ val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
+
+ val viewCenterProvider = StatusBarViewsCenterProvider()
+ val viewsToAnimate = arrayOf(
+ statusBarLeftSide,
+ systemIconArea
+ )
+
+ animationController.init(viewsToAnimate, viewCenterProvider)
+
+ mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
+ val widthChanged = right - left != oldRight - oldLeft
+ if (widthChanged) {
+ statusBarMoveFromCenterAnimationController.onStatusBarWidthChanged()
+ }
+ }
+ }
+ }
+
+ private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
+ override fun getViewCenter(view: View, outPoint: Point) =
+ when (view.id) {
+ R.id.status_bar_left_side -> {
+ // items aligned to the start, return start center point
+ getViewEdgeCenter(view, outPoint, isStart = true)
+ }
+ R.id.system_icon_area -> {
+ // items aligned to the end, return end center point
+ getViewEdgeCenter(view, outPoint, isStart = false)
+ }
+ else -> super.getViewCenter(view, outPoint)
+ }
+
+ /**
+ * Returns start or end (based on [isStart]) center point of the view
+ */
+ private fun getViewEdgeCenter(view: View, outPoint: Point, isStart: Boolean) {
+ val isRtl = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ val isLeftEdge = isRtl xor isStart
+
+ val viewLocation = IntArray(2)
+ view.getLocationOnScreen(viewLocation)
+
+ val viewX = viewLocation[0]
+ val viewY = viewLocation[1]
+
+ outPoint.x = viewX + if (isLeftEdge) view.height / 2 else view.width - view.height / 2
+ outPoint.y = viewY + view.height / 2
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 575a3c4..50db136 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -454,6 +454,7 @@
@Nullable
protected LockscreenWallpaper mLockscreenWallpaper;
private final AutoHideController mAutoHideController;
+ private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
private final Point mCurrentDisplaySize = new Point();
@@ -536,6 +537,7 @@
private final FeatureFlags mFeatureFlags;
private final UnfoldTransitionConfig mUnfoldTransitionConfig;
private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
+ private final Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimation;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
@@ -742,6 +744,7 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
@@ -768,6 +771,7 @@
BrightnessSlider.Factory brightnessSliderFactory,
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Lazy<StatusBarMoveFromCenterAnimationController> statusBarUnfoldAnimationController,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -840,6 +844,7 @@
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
+ mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mStatusBarComponentFactory = statusBarComponentFactory;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
@@ -860,6 +865,7 @@
mBrightnessSliderFactory = brightnessSliderFactory;
mUnfoldTransitionConfig = unfoldTransitionConfig;
mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
+ mMoveFromCenterAnimation = statusBarUnfoldAnimationController;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
@@ -1141,8 +1147,16 @@
sendInitialExpansionAmount(listener);
}
+ StatusBarMoveFromCenterAnimationController moveFromCenterAnimation = null;
+ if (mUnfoldTransitionConfig.isEnabled()) {
+ moveFromCenterAnimation = mMoveFromCenterAnimation.get();
+ }
mPhoneStatusBarViewController =
- new PhoneStatusBarViewController(mStatusBarView, mCommandQueue);
+ new PhoneStatusBarViewController(
+ mStatusBarView,
+ mCommandQueue,
+ moveFromCenterAnimation,
+ this::onPanelExpansionStateChanged);
mPhoneStatusBarViewController.init();
mBatteryMeterViewController = new BatteryMeterViewController(
@@ -1207,6 +1221,7 @@
mStatusBarStateController,
() -> Optional.of(this),
mCommandQueue,
+ mCollapsedStatusBarFragmentLogger,
mOperatorNameViewControllerFactory
),
CollapsedStatusBarFragment.TAG)
@@ -1412,6 +1427,15 @@
}
}
+ private void onPanelExpansionStateChanged() {
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().onStatusBarPanelStateChanged();
+ }
+ if (getNotificationPanelViewController() != null) {
+ getNotificationPanelViewController().updateSystemUiStateFlags();
+ }
+ }
+
@NonNull
@Override
public Lifecycle getLifecycle() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index 6a510c9..5301b25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -57,6 +57,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -98,6 +99,7 @@
private final VibratorHelper mVibratorHelper;
private final Optional<Vibrator> mVibratorOptional;
private final LightBarController mLightBarController;
+ private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
@@ -134,6 +136,7 @@
VibratorHelper vibratorHelper,
Optional<Vibrator> vibratorOptional,
LightBarController lightBarController,
+ DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId) {
mStatusBar = statusBar;
@@ -159,6 +162,7 @@
mVibratorHelper = vibratorHelper;
mVibratorOptional = vibratorOptional;
mLightBarController = lightBarController;
+ mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
@@ -267,7 +271,17 @@
if (displayId != mDisplayId) {
return;
}
+
+ int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+ Log.d(StatusBar.TAG,
+ mDisableFlagsLogger.getDisableFlagsString(
+ /* old= */ new DisableFlagsLogger.DisableState(
+ mStatusBar.getDisabled1(), mStatusBar.getDisabled2()),
+ /* new= */ new DisableFlagsLogger.DisableState(
+ state1, state2BeforeAdjustment),
+ /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
+ state1, state2)));
final int old1 = mStatusBar.getDisabled1();
final int diff1 = state1 ^ old1;
@@ -277,43 +291,6 @@
final int diff2 = state2 ^ old2;
mStatusBar.setDisabled2(state2);
- if (StatusBar.DEBUG) {
- Log.d(StatusBar.TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
- old1, state1, diff1));
- Log.d(StatusBar.TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
- old2, state2, diff2));
- }
-
- StringBuilder flagdbg = new StringBuilder();
- flagdbg.append("disable<");
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
- flagdbg.append("> disable2<");
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
- flagdbg.append('>');
- Log.d(StatusBar.TAG, flagdbg.toString());
-
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
mShadeController.animateCollapsePanels();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 6a63156..88a7dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -114,6 +114,14 @@
}
}
+ private void refreshIconGroups() {
+ for (int i = mIconGroups.size() - 1; i >= 0; --i) {
+ IconManager group = mIconGroups.get(i);
+ removeIconGroup(group);
+ addIconGroup(group);
+ }
+ }
+
/** */
@Override
public void removeIconGroup(IconManager group) {
@@ -468,5 +476,6 @@
@Override
public void onDensityOrFontScaleChanged() {
loadDimens();
+ refreshIconGroups();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4316ccf..7725443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -197,6 +197,7 @@
private boolean mLastGesturalNav;
private boolean mLastIsDocked;
private boolean mLastPulsing;
+ private boolean mLastAnimatedToSleep;
private int mLastBiometricMode;
private boolean mQsExpanded;
private boolean mAnimatedToSleep;
@@ -1012,6 +1013,7 @@
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
+ mLastAnimatedToSleep = mAnimatedToSleep;
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
@@ -1055,7 +1057,7 @@
boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
|| mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
- return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
+ return (!mLastAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
new file mode 100644
index 0000000..8af03aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarMoveFromCenterAnimationController @Inject constructor(
+ private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ private val windowManager: WindowManager
+) {
+
+ private lateinit var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator
+
+ fun init(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
+ moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
+ viewCenterProvider = viewCenterProvider)
+
+ unfoldTransitionProgressProvider.addCallback(object : TransitionProgressListener {
+ override fun onTransitionStarted() {
+ moveFromCenterAnimator.updateDisplayProperties()
+
+ viewsToAnimate.forEach {
+ moveFromCenterAnimator.registerViewForAnimation(it)
+ }
+ }
+
+ override fun onTransitionFinished() {
+ moveFromCenterAnimator.onTransitionFinished()
+ moveFromCenterAnimator.clearRegisteredViews()
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ moveFromCenterAnimator.onTransitionProgress(progress)
+ }
+ })
+ }
+
+ fun onStatusBarWidthChanged() {
+ moveFromCenterAnimator.updateViewPositions()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index f8120a8..143aaba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -8,13 +8,13 @@
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
-import com.android.systemui.statusbar.StatusBarState
import android.view.View
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.notification.AnimatableProperty
import com.android.systemui.statusbar.notification.PropertyAnimator
@@ -61,6 +61,7 @@
private var shouldAnimateInKeyguard = false
private var lightRevealAnimationPlaying = false
private var aodUiAnimationPlaying = false
+ private var callbacks = HashSet<Callback>()
/**
* The result of our decision whether to play the screen off animation in
@@ -72,11 +73,17 @@
private val lightRevealAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
duration = LIGHT_REVEAL_ANIMATION_DURATION
interpolator = Interpolators.LINEAR
- addUpdateListener { lightRevealScrim.revealAmount = it.animatedValue as Float }
+ addUpdateListener {
+ lightRevealScrim.revealAmount = it.animatedValue as Float
+ sendUnlockedScreenOffProgressUpdate(
+ 1f - (it.animatedFraction as Float),
+ 1f - (it.animatedValue as Float))
+ }
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator?) {
lightRevealScrim.revealAmount = 1f
lightRevealAnimationPlaying = false
+ sendUnlockedScreenOffProgressUpdate(0f, 0f)
}
override fun onAnimationEnd(animation: Animator?) {
@@ -243,7 +250,21 @@
return true
}
- /**
+ fun addCallback(callback: Callback) {
+ callbacks.add(callback)
+ }
+
+ fun removeCallback(callback: Callback) {
+ callbacks.remove(callback)
+ }
+
+ fun sendUnlockedScreenOffProgressUpdate(linear: Float, eased: Float) {
+ callbacks.forEach {
+ it.onUnlockedScreenOffProgressUpdate(linear, eased)
+ }
+ }
+
+/**
* Whether we're doing the light reveal animation or we're done with that and animating in the
* AOD UI.
*/
@@ -262,4 +283,8 @@
fun isScreenOffLightRevealAnimationPlaying(): Boolean {
return lightRevealAnimationPlaying
}
+
+ interface Callback {
+ fun onUnlockedScreenOffProgressUpdate(linear: Float, eased: Float)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 63ee701..c45068e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -72,6 +72,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.DozeScrimController;
import com.android.systemui.statusbar.phone.DozeServiceHost;
@@ -90,6 +91,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
@@ -187,6 +189,7 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
@@ -213,6 +216,7 @@
BrightnessSlider.Factory brightnessSliderFactory,
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -282,6 +286,7 @@
dozeScrimController,
volumeComponent,
commandQueue,
+ collapsedStatusBarFragmentLogger,
statusBarComponentFactory,
pluginManager,
splitScreenOptional,
@@ -307,6 +312,7 @@
brightnessSliderFactory,
unfoldTransitionConfig,
unfoldLightRevealOverlayAnimation,
+ statusBarMoveFromCenterAnimation,
ongoingCallController,
animationScheduler,
locationPublisher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index b6c683f..793a9e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -27,6 +27,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardVisibilityHelper;
@@ -59,7 +60,7 @@
* Manages the user switch on the Keyguard that is used for opening the QS user panel.
*/
@KeyguardUserSwitcherScope
-public class KeyguardQsUserSwitchController extends ViewController<UserAvatarView> {
+public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> {
private static final String TAG = "KeyguardQsUserSwitchController";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -81,6 +82,7 @@
private final FeatureFlags mFeatureFlags;
private final UserSwitchDialogController mUserSwitchDialogController;
private NotificationPanelViewController mNotificationPanelViewController;
+ private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
// State info for the user switch and keyguard
@@ -116,7 +118,7 @@
@Inject
public KeyguardQsUserSwitchController(
- UserAvatarView view,
+ FrameLayout view,
Context context,
@Main Resources resources,
ScreenLifecycle screenLifecycle,
@@ -154,6 +156,7 @@
protected void onInit() {
super.onInit();
if (DEBUG) Log.d(TAG, "onInit");
+ mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar);
mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
@@ -161,11 +164,10 @@
}
};
- mView.setOnClickListener(v -> {
+ mUserAvatarView.setOnClickListener(v -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
}
-
if (isListAnimating()) {
return;
}
@@ -178,7 +180,7 @@
}
});
- mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
@@ -252,12 +254,12 @@
R.string.accessibility_multi_user_switch_switcher);
}
- if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
- mView.setContentDescription(contentDescription);
+ if (!TextUtils.equals(mUserAvatarView.getContentDescription(), contentDescription)) {
+ mUserAvatarView.setContentDescription(contentDescription);
}
int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL;
- mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
+ mUserAvatarView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
}
Drawable getCurrentUserIcon() {
@@ -284,7 +286,7 @@
* Get the height of the keyguard user switcher view when closed.
*/
public int getUserIconHeight() {
- return mView.getHeight();
+ return mUserAvatarView.getHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 3490e15..a543c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -91,8 +91,7 @@
private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
- // @VisibleForDemoMode
- Map<String, MobileIconGroup> mNetworkToIconLookup;
+ private Map<String, MobileIconGroup> mNetworkToIconLookup;
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
@@ -108,8 +107,6 @@
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- private MobileStatusTracker.Callback mCallback;
- private RegistrationCallback mRegistrationCallback;
private int mLastWwanLevel;
private int mLastWlanLevel;
private int mLastWlanCrossSimLevel;
@@ -121,6 +118,82 @@
// Where to copy the next state into.
private int mMobileStatusHistoryIndex;
+ private final MobileStatusTracker.Callback mMobileCallback =
+ new MobileStatusTracker.Callback() {
+ private String mLastStatus;
+
+ @Override
+ public void onMobileStatusChanged(boolean updateTelephony,
+ MobileStatus mobileStatus) {
+ if (Log.isLoggable(mTag, Log.DEBUG)) {
+ Log.d(mTag, "onMobileStatusChanged="
+ + " updateTelephony=" + updateTelephony
+ + " mobileStatus=" + mobileStatus.toString());
+ }
+ String currentStatus = mobileStatus.toString();
+ if (!currentStatus.equals(mLastStatus)) {
+ mLastStatus = currentStatus;
+ String status = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(currentStatus)
+ .toString();
+ recordLastMobileStatus(status);
+ }
+ updateMobileStatus(mobileStatus);
+ if (updateTelephony) {
+ updateTelephony();
+ } else {
+ notifyListenersIfNecessary();
+ }
+ }
+ };
+
+ private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
+ @Override
+ public void onRegistered(ImsRegistrationAttributes attributes) {
+ Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+ int imsTransportType = attributes.getTransportType();
+ int registrationAttributes = attributes.getAttributeFlags();
+ if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ if (registrationAttributes == 0) {
+ mImsType = IMS_TYPE_WLAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+ getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ } else if (registrationAttributes
+ == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+ mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+ getCallStrengthDescription(
+ mLastWlanCrossSimLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ }
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ Log.d(mTag, "onDeregistered: " + "info=" + info);
+ mImsType = IMS_TYPE_WWAN;
+ IconState statusIcon = new IconState(
+ true,
+ getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+ getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+ };
+
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(
@@ -144,8 +217,8 @@
mPhone = phone;
mDefaults = defaults;
mSubscriptionInfo = info;
- mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator)
- .toString();
+ mNetworkNameSeparator = getTextIfExists(
+ R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
mReceiverHandler = new Handler(receiverLooper);
@@ -165,83 +238,9 @@
updateTelephony();
}
};
- mCallback = new MobileStatusTracker.Callback() {
- private String mLastStatus;
-
- @Override
- public void onMobileStatusChanged(boolean updateTelephony,
- MobileStatus mobileStatus) {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "onMobileStatusChanged="
- + " updateTelephony=" + updateTelephony
- + " mobileStatus=" + mobileStatus.toString());
- }
- String currentStatus = mobileStatus.toString();
- if (!currentStatus.equals(mLastStatus)) {
- mLastStatus = currentStatus;
- String status = new StringBuilder()
- .append(SSDF.format(System.currentTimeMillis())).append(",")
- .append(currentStatus)
- .toString();
- recordLastMobileStatus(status);
- }
- updateMobileStatus(mobileStatus);
- if (updateTelephony) {
- updateTelephony();
- } else {
- notifyListenersIfNecessary();
- }
- }
- };
-
- mRegistrationCallback = new RegistrationCallback() {
- @Override
- public void onRegistered(ImsRegistrationAttributes attributes) {
- Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
- int imsTransportType = attributes.getTransportType();
- int registrationAttributes = attributes.getAttributeFlags();
- if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- if (registrationAttributes == 0) {
- mImsType = IMS_TYPE_WLAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (registrationAttributes
- == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
- mImsType = IMS_TYPE_WLAN_CROSS_SIM;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(
- mLastWlanCrossSimLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- Log.d(mTag, "onDeregistered: " + "info=" + info);
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- };
mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
- info, mDefaults, mCallback);
+ info, mDefaults, mMobileCallback);
mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled();
mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
}
@@ -385,89 +384,83 @@
if (mCurrentState.inetCondition == 0) {
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
- && mCurrentState.userSetup;
+
+ final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
+ final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
+
+ MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+ sbInfo.icon,
+ qsInfo.icon,
+ sbInfo.ratTypeIcon,
+ qsInfo.ratTypeIcon,
+ mCurrentState.hasActivityIn(),
+ mCurrentState.hasActivityOut(),
+ dataContentDescription,
+ dataContentDescriptionHtml,
+ qsInfo.description,
+ mSubscriptionInfo.getSubscriptionId(),
+ mCurrentState.roaming,
+ sbInfo.showTriangle);
+ callback.setMobileDataIndicators(mobileDataIndicators);
+ }
+
+ private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) {
+ int qsTypeIcon = 0;
+ IconState qsIcon = null;
+ CharSequence qsDescription = null;
+
+ boolean pm = mProviderModelSetting || mProviderModelBehavior;
+ if (mCurrentState.dataSim) {
+ // If using provider model behavior, only show QS icons if the state is also default
+ if (pm && !mCurrentState.isDefault) {
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ }
+
+ if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
+ qsTypeIcon = dataTypeIcon;
+ }
+
+ boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency;
+ qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription);
+
+ if (!mCurrentState.isEmergency) {
+ qsDescription = mCurrentState.networkName;
+ }
+ }
+
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ }
+
+ private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
+ final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
+ boolean showTriangle = false;
+ int typeIcon = 0;
+ IconState statusIcon = null;
if (mProviderModelBehavior) {
- // Show icon in QS when we are connected or data is disabled.
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
-
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- CharSequence description = null;
- // Only send data sim callbacks to QS.
- if (mCurrentState.dataSim && mCurrentState.isDefault) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(mCurrentState.enabled
- && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- boolean activityIn = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityIn;
- boolean activityOut = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityOut;
- showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
- boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
- showDataIcon |= mCurrentState.roaming;
- IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
+ boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
+ && (mCurrentState.dataSim && mCurrentState.isDefault);
+ typeIcon =
+ (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ showDataIconStatusBar |= mCurrentState.roaming;
+ statusIcon = new IconState(
+ showDataIconStatusBar && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
- statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
- description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming, showTriangle);
- callback.setMobileDataIndicators(mobileDataIndicators);
+
+ showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
} else {
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
- IconState statusIcon = new IconState(
+ statusIcon = new IconState(
mCurrentState.enabled && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- CharSequence description = null;
- // Only send data sim callbacks to QS.
- if (mProviderModelSetting) {
- if (mCurrentState.dataSim && mCurrentState.isDefault) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.isEmergency,
- getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- } else {
- if (mCurrentState.dataSim) {
- qsTypeIcon =
- (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
- qsIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.isEmergency,
- getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
- }
-
- boolean activityIn = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityIn;
- boolean activityOut = mCurrentState.dataConnected
- && !mCurrentState.carrierNetworkChangeMode
- && mCurrentState.activityOut;
- showDataIcon &= mCurrentState.isDefault || dataDisabled;
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
- boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
- MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
- statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
- description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming, showTriangle);
- callback.setMobileDataIndicators(mobileDataIndicators);
+ boolean showDataIconInStatusBar =
+ (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+ typeIcon =
+ (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
}
+
+ return new SbInfo(showTriangle, typeIcon, statusIcon);
}
@Override
@@ -841,12 +834,15 @@
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
+ pw.println(" mProviderModelSetting=" + mProviderModelSetting + ",");
+ pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mServiceState=" + mServiceState + ",");
pw.println(" mSignalStrength=" + mSignalStrength + ",");
pw.println(" mTelephonyDisplayInfo=" + mTelephonyDisplayInfo + ",");
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
+ pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
pw.println(" MobileStatusHistory");
int size = 0;
for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -862,4 +858,31 @@
+ mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
}
}
+
+ /** Box for QS icon info */
+ private static final class QsInfo {
+ final int ratTypeIcon;
+ final IconState icon;
+ final CharSequence description;
+
+ QsInfo(int typeIcon, IconState iconState, CharSequence desc) {
+ ratTypeIcon = typeIcon;
+ icon = iconState;
+ description = desc;
+ }
+ }
+
+ /** Box for StatusBar icon info */
+ private static final class SbInfo {
+ final boolean showTriangle;
+ final int ratTypeIcon;
+ final IconState icon;
+
+ SbInfo(boolean show, int typeIcon, IconState iconState) {
+ showTriangle = show;
+ ratTypeIcon = typeIcon;
+ icon = iconState;
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index eeea699..9aec903 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -81,7 +81,7 @@
.append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
.append(",activityIn=").append(activityIn)
.append(",activityOut=").append(activityOut)
- .append(",description=").append(description)
+ .append(",qsDescription=").append(description)
.append(",isTransient=").append(isTransient)
.append(",statusLabel=").append(statusLabel)
.append(']').toString();
@@ -100,8 +100,7 @@
public boolean activityOut;
public CharSequence typeContentDescription;
public CharSequence typeContentDescriptionHtml;
- public CharSequence description;
- public boolean isWide;
+ public CharSequence qsDescription;
public int subId;
public boolean roaming;
public boolean showTriangle;
@@ -109,7 +108,7 @@
public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml,
- CharSequence description, boolean isWide, int subId, boolean roaming,
+ CharSequence qsDescription, int subId, boolean roaming,
boolean showTriangle) {
this.statusIcon = statusIcon;
this.qsIcon = qsIcon;
@@ -119,8 +118,7 @@
this.activityOut = activityOut;
this.typeContentDescription = typeContentDescription;
this.typeContentDescriptionHtml = typeContentDescriptionHtml;
- this.description = description;
- this.isWide = isWide;
+ this.qsDescription = qsDescription;
this.subId = subId;
this.roaming = roaming;
this.showTriangle = showTriangle;
@@ -137,8 +135,7 @@
.append(",activityOut=").append(activityOut)
.append(",typeContentDescription=").append(typeContentDescription)
.append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
- .append(",description=").append(description)
- .append(",isWide=").append(isWide)
+ .append(",description=").append(qsDescription)
.append(",subId=").append(subId)
.append(",roaming=").append(roaming)
.append(",showTriangle=").append(showTriangle)
@@ -186,11 +183,13 @@
default void setCallIndicator(IconState statusIcon, int subId) {}
}
- public interface EmergencyListener {
+ /** */
+ interface EmergencyListener {
void setEmergencyCallsOnly(boolean emergencyOnly);
}
- public static class IconState {
+ /** */
+ class IconState {
public final boolean visible;
public final int icon;
public final String contentDescription;
@@ -220,7 +219,7 @@
* Tracks changes in access points. Allows listening for changes, scanning for new APs,
* and connecting to new ones.
*/
- public interface AccessPointController {
+ interface AccessPointController {
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
@@ -230,7 +229,7 @@
boolean canConfigWifi();
boolean canConfigMobileData();
- public interface AccessPointCallback {
+ interface AccessPointCallback {
void onAccessPointsChanged(List<WifiEntry> accessPoints);
void onSettingsActivityTriggered(Intent settingsIntent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index 22fd93e..2d47c8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -71,7 +71,11 @@
if (mNextAlarm != null) {
pw.println(new Date(mNextAlarm.getTriggerTime()));
pw.print(" PendingIntentPkg=");
- pw.println(mNextAlarm.getShowIntent().getCreatorPackage());
+ if (mNextAlarm.getShowIntent() != null) {
+ pw.println(mNextAlarm.getShowIntent().getCreatorPackage());
+ } else {
+ pw.println("showIntent=null");
+ }
} else {
pw.println("null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index fc19564..3f7ddc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -163,7 +163,7 @@
int qsTypeIcon = 0;
IconState qsIcon = null;
if (sbVisible) {
- qsTypeIcon = icons.qsDataType;
+ qsTypeIcon = icons.dataType;
qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(),
contentDescription);
}
@@ -172,7 +172,7 @@
MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
- dataContentDescriptionHtml, description, icons.isWide,
+ dataContentDescriptionHtml, description,
mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true
);
callback.setMobileDataIndicators(mobileDataIndicators);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index a5871f0..d5ee4fa 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -37,6 +37,7 @@
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayIdentifier;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Color;
import android.net.Uri;
@@ -47,9 +48,11 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.TypedValue;
import androidx.annotation.NonNull;
+import com.android.internal.graphics.ColorUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -59,6 +62,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.monet.ColorScheme;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -71,6 +75,7 @@
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -107,6 +112,7 @@
private DeviceProvisionedController mDeviceProvisionedController;
private WallpaperColors mCurrentColors;
private WallpaperManager mWallpaperManager;
+ private ColorScheme mColorScheme;
// If fabricated overlays were already created for the current theme.
private boolean mNeedsOverlayCreation;
// Dominant color extracted from wallpaper, NOT the color used on the overlay
@@ -408,25 +414,47 @@
* Return the main theme color from a given {@link WallpaperColors} instance.
*/
protected int getNeutralColor(@NonNull WallpaperColors wallpaperColors) {
- return wallpaperColors.getPrimaryColor().toArgb();
+ return ColorScheme.getSeedColor(wallpaperColors);
}
protected int getAccentColor(@NonNull WallpaperColors wallpaperColors) {
- Color accentCandidate = wallpaperColors.getSecondaryColor();
- if (accentCandidate == null) {
- accentCandidate = wallpaperColors.getTertiaryColor();
- }
- if (accentCandidate == null) {
- accentCandidate = wallpaperColors.getPrimaryColor();
- }
- return accentCandidate.toArgb();
+ return ColorScheme.getSeedColor(wallpaperColors);
}
/**
* Given a color candidate, return an overlay definition.
*/
protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
- return null;
+ boolean nightMode = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+
+ mColorScheme = new ColorScheme(color, nightMode);
+ List<Integer> colorShades = type == ACCENT
+ ? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
+ String name = type == ACCENT ? "accent" : "neutral";
+ int paletteSize = mColorScheme.getAccent1().size();
+ FabricatedOverlay.Builder overlay =
+ new FabricatedOverlay.Builder("com.android.systemui", name, "android");
+ for (int i = 0; i < colorShades.size(); i++) {
+ int luminosity = i % paletteSize;
+ int paletteIndex = i / paletteSize + 1;
+ String resourceName;
+ switch (luminosity) {
+ case 0:
+ resourceName = "android:color/system_" + name + paletteIndex + "_10";
+ break;
+ case 1:
+ resourceName = "android:color/system_" + name + paletteIndex + "_50";
+ break;
+ default:
+ int l = luminosity - 1;
+ resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00";
+ }
+ overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8,
+ ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF));
+ }
+
+ return overlay.build();
}
private void updateThemeOverlays() {
@@ -526,7 +554,7 @@
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
Collectors.joining(", ")));
}
- Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
+ Runnable overlaysAppliedRunnable = this::onOverlaysApplied;
if (mNeedsOverlayCreation) {
mNeedsOverlayCreation = false;
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
@@ -549,6 +577,7 @@
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
+ pw.println("mColorScheme=" + mColorScheme);
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 2b4b49b..d44fb76 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -220,6 +220,12 @@
}
@Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.wallet_activity_options_menu, menu);
return super.onCreateOptionsMenu(menu);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 06b0bb2..8d615f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -226,7 +226,7 @@
}
@Test
- public void testDetachRemovesSmartspaceView() {
+ public void testDetachDisconnectsSmartspace() {
when(mSmartspaceController.isEnabled()).thenReturn(true);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
@@ -237,7 +237,7 @@
verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
- verify(mView).removeView(mFakeSmartspaceView);
+ verify(mSmartspaceController).disconnect();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index b6d1e42..619d48d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -257,9 +258,10 @@
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */,
- 540 /* sensorLocationX */,
- 1600 /* sensorLocationY */,
- 100 /* sensorRadius */);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ 540 /* sensorLocationX */,
+ 1600 /* sensorLocationY */,
+ 100 /* sensorRadius */)));
}
public class TestableView extends AuthBiometricFaceToFingerprintView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 8f5eefc..5c73077 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -23,6 +23,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
@@ -59,6 +60,7 @@
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
@Mock private lateinit var udfpsController: UdfpsController
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
@Before
fun setUp() {
@@ -76,6 +78,7 @@
bypassController,
biometricUnlockController,
udfpsControllerProvider,
+ statusBarStateController,
rippleView
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 977b05c..01e60e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics
+import android.graphics.Rect
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManagerGlobal
@@ -30,8 +31,12 @@
import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
import android.view.DisplayInfo
import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowInsets
import android.view.WindowManager
+import android.view.WindowMetrics
import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -42,9 +47,13 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -66,11 +75,13 @@
@Mock
lateinit var windowManager: WindowManager
@Mock
- lateinit var sidefpsView: SidefpsView
+ lateinit var sidefpsView: View
@Mock
lateinit var displayManager: DisplayManager
@Mock
lateinit var handler: Handler
+ @Captor
+ lateinit var overlayCaptor: ArgumentCaptor<View>
private val executor = FakeExecutor(FakeSystemClock())
private lateinit var overlayController: ISidefpsController
@@ -79,6 +90,8 @@
@Before
fun setup() {
`when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+ `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+ .thenReturn(mock(LottieAnimationView::class.java))
`when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
listOf(
FingerprintSensorPropertiesInternal(
@@ -99,6 +112,9 @@
DEFAULT_DISPLAY_ADJUSTMENTS
)
)
+ `when`(windowManager.maximumWindowMetrics).thenReturn(
+ WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
+ )
sideFpsController = SidefpsController(
mContext, layoutInflater, fingerprintManager, windowManager, executor,
@@ -121,4 +137,44 @@
executor.runAllReady()
verify(displayManager).unregisterDisplayListener(any())
}
+
+ @Test
+ fun testShowsAndHides() {
+ overlayController.show()
+ executor.runAllReady()
+
+ verify(windowManager).addView(overlayCaptor.capture(), any())
+
+ reset(windowManager)
+ overlayController.hide()
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ verify(windowManager).removeView(eq(overlayCaptor.value))
+ }
+
+ @Test
+ fun testShowsOnce() {
+ repeat(5) {
+ overlayController.show()
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun testHidesOnce() {
+ overlayController.show()
+ executor.runAllReady()
+
+ repeat(5) {
+ overlayController.hide()
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager).removeView(any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index f5c6f98..1197660 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.Mockito.when;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
@@ -57,7 +58,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.Execution;
@@ -123,8 +124,6 @@
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private KeyguardViewMediator mKeyguardViewMediator;
- @Mock
private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
@Mock
private FalsingManager mFalsingManager;
@@ -152,6 +151,8 @@
private ConfigurationController mConfigurationController;
@Mock
private SystemClock mSystemClock;
+ @Mock
+ private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private FakeExecutor mFgExecutor;
@@ -191,6 +192,7 @@
when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
.thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
when(mEnrollView.getContext()).thenReturn(mContext);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -221,7 +223,6 @@
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
- mKeyguardViewMediator,
mFalsingManager,
mPowerManager,
mAccessibilityManager,
@@ -235,7 +236,8 @@
mDisplayManager,
mHandler,
mConfigurationController,
- mSystemClock);
+ mSystemClock,
+ mUnlockedScreenOffAnimationController);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -256,7 +258,7 @@
@Test
public void dozeTimeTick() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
mUdfpsController.dozeTimeTick();
verify(mUdfpsView).dozeTimeTick();
@@ -271,7 +273,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -294,7 +296,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -317,7 +319,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -340,7 +342,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -362,7 +364,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_MOVE is received
@@ -384,7 +386,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN multiple touches are received
@@ -404,7 +406,7 @@
@Test
public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(eq(mUdfpsView), any());
}
@@ -412,7 +414,7 @@
@Test
public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).removeView(eq(mUdfpsView));
@@ -422,7 +424,7 @@
public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
// WHEN the overlay is hidden
@@ -436,7 +438,7 @@
@Test
public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
@@ -455,7 +457,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -479,7 +481,7 @@
public void aodInterrupt() throws RemoteException {
// GIVEN that the overlay is showing and screen is on
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
@@ -496,7 +498,7 @@
public void cancelAodInterrupt() throws RemoteException {
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -511,7 +513,7 @@
public void aodInterruptTimeout() throws RemoteException {
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -527,7 +529,7 @@
public void aodInterruptScreenOff() throws RemoteException {
// GIVEN screen off
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
@@ -546,7 +548,7 @@
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index 88b4039..27755ede 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -61,8 +62,9 @@
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
- sensorRadius);
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ List.of(new SensorLocationInternal("" /* displayId */,
+ sensorLocationX, sensorLocationY, sensorRadius)));
assertEquals(970,
UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait(
@@ -125,8 +127,9 @@
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
- sensorRadius);
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ List.of(new SensorLocationInternal("" /* displayId */,
+ sensorLocationX, sensorLocationY, sensorRadius)));
assertEquals(1205,
UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index be3e535..2821f3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -89,6 +90,8 @@
@Mock
private ConfigurationController mConfigurationController;
@Mock
+ private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock
private UdfpsController mUdfpsController;
private FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -121,13 +124,12 @@
Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
- mExecutor,
mDumpManager,
- mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index a32cb9b..d6226aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -43,7 +43,6 @@
when(params.singleTapUsesProx()).thenReturn(true);
when(params.longPressUsesProx()).thenReturn(true);
when(params.getQuickPickupAodDuration()).thenReturn(500);
- when(params.brightnessUsesProx()).thenReturn(true);
doneHolder[0] = true;
return params;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index deb7d31..e0520b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
@@ -43,7 +44,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -114,8 +114,6 @@
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */,
mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
-
- mScreen.onScreenState(Display.STATE_ON);
}
@Test
@@ -127,18 +125,9 @@
}
@Test
- public void testAod_usesLightSensor() {
- mScreen.onScreenState(Display.STATE_DOZE);
- waitForSensorManager();
-
- mSensor.sendSensorEvent(3);
-
- assertEquals(3, mServiceFake.screenBrightness);
- }
-
- @Test
public void testAod_usesDebugValue() throws Exception {
- mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
waitForSensorManager();
Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
@@ -161,71 +150,53 @@
}
@Test
- public void testAodDocked_doNotSelectivelyUseProx_usesLightSensor() {
- // GIVEN the device doesn't need to selectively register for prox sensors and
- // brightness sensor uses prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
-
+ public void doze_doesNotUseLightSensor() {
// GIVEN the device is docked and the display state changes to ON
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE);
waitForSensorManager();
// WHEN new sensor event sent
mSensor.sendSensorEvent(3);
- // THEN brightness is updated
- assertEquals(3, mServiceFake.screenBrightness);
- }
-
- @Test
- public void testAodDocked_brightnessDoesNotUseProx_usesLightSensor() {
- // GIVEN the device doesn't need to selectively register for prox sensors but
- // the brightness sensor doesn't use prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(false);
-
- // GIVEN the device is docked and the display state changes to ON
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
- waitForSensorManager();
-
- // WHEN new sensor event sent
- mSensor.sendSensorEvent(3);
-
- // THEN brightness is updated
- assertEquals(3, mServiceFake.screenBrightness);
- }
-
-
- @Test
- public void testAodDocked_noProx_brightnessUsesProx_doNotUseLightSensor() {
- final int startBrightness = mServiceFake.screenBrightness;
-
- // GIVEN the device needs to selectively register for prox sensors and
- // the brightness sensor uses prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
-
- // GIVEN the device is docked and the display state is on
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
- waitForSensorManager();
-
- // WHEN new sensor event sent
- mSensor.sendSensorEvent(3);
-
- // THEN brightness is NOT changed
+ // THEN brightness is NOT changed, it's set to the default brightness
assertNotSame(3, mServiceFake.screenBrightness);
- assertEquals(startBrightness, mServiceFake.screenBrightness);
+ assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+ }
+
+ @Test
+ public void aod_usesLightSensor() {
+ // GIVEN the device is docked and the display state changes to ON
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is updated
+ assertEquals(3, mServiceFake.screenBrightness);
+ }
+
+ @Test
+ public void docked_usesLightSensor() {
+ // GIVEN the device is docked and the display state changes to ON
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.transitionTo(DOZE_AOD, DOZE_AOD_DOCKED);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is updated
+ assertEquals(3, mServiceFake.screenBrightness);
}
@Test
public void testPausingAod_doesNotResetBrightness() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -266,18 +237,6 @@
}
@Test
- public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.onScreenState(Display.STATE_DOZE);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- waitForSensorManager();
-
- mSensor.sendSensorEvent(1);
-
- assertEquals(1, mServiceFake.screenBrightness);
- }
-
- @Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
@@ -287,15 +246,12 @@
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
- mScreen.onScreenState(Display.STATE_DOZE);
- mScreen.onScreenState(Display.STATE_OFF);
}
@Test
public void testNoBrightnessDeliveredAfterFinish() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
mScreen.transitionTo(DOZE_AOD, FINISH);
waitForSensorManager();
@@ -308,7 +264,6 @@
public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -322,7 +277,6 @@
public void pausingAod_unblanksAfterSensor() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
@@ -334,7 +288,6 @@
reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
@@ -344,7 +297,6 @@
public void pausingAod_unblanksIfSensorWasAlwaysReady() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 41d7fd6..150ab77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -21,6 +21,7 @@
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -34,6 +35,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +47,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLockFake;
import com.android.systemui.utils.os.FakeHandler;
@@ -56,6 +60,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import javax.inject.Provider;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeScreenStateTest extends SysuiTestCase {
@@ -68,17 +74,29 @@
private DozeParameters mDozeParameters;
private WakeLockFake mWakeLock;
private DozeScreenState mScreen;
+ @Mock
+ private Provider<UdfpsController> mUdfpsControllerProvider;
+ @Mock
+ private AuthController mAuthController;
+ @Mock
+ private UdfpsController mUdfpsController;
+ @Mock
+ private DozeLog mDozeLog;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mUdfpsControllerProvider.get()).thenReturn(mUdfpsController);
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+
mServiceFake = new DozeServiceFake();
mHandlerFake = new FakeHandler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
- mWakeLock);
+ mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog);
}
@Test
@@ -233,4 +251,56 @@
assertEquals(Display.STATE_OFF, mServiceFake.screenState);
}
+ @Test
+ public void testDelayEnterDozeScreenState_whenUdfpsFingerDown() {
+ // GIVEN AOD is initialized
+ when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ mHandlerFake.setMode(QUEUEING);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mHandlerFake.dispatchQueuedMessages();
+
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+
+ // WHEN udfps is activated (fingerDown)
+ when(mUdfpsController.isFingerDown()).thenReturn(true);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state doesn't immediately change
+ assertEquals(Display.STATE_ON, mServiceFake.screenState);
+
+ // WHEN udfpsController finger is no longer down and the queued messages are run
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state will change
+ assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
+ }
+
+ @Test
+ public void testDelayExitPulsingScreenState_whenUdfpsFingerDown() {
+ // GIVEN we're pulsing
+ when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ mHandlerFake.setMode(QUEUEING);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.transitionTo(DOZE_AOD, DOZE_REQUEST_PULSE);
+ mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING);
+ mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // WHEN udfps is activated while are transitioning back to DOZE_AOD
+ mScreen.transitionTo(DOZE_PULSE_DONE, DOZE_AOD);
+ when(mUdfpsController.isFingerDown()).thenReturn(true);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state doesn't immediately change
+ assertEquals(Display.STATE_ON, mServiceFake.screenState);
+
+ // WHEN udfpsController finger is no longer down and the queued messages are run
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state will change
+ assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 10997fa..9577c7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -83,6 +84,8 @@
private AuthController mAuthController;
@Mock
private UiEventLogger mUiEventLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -114,7 +117,7 @@
mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
- mAuthController, mExecutor, mUiEventLogger);
+ mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -217,6 +220,32 @@
}
@Test
+ public void testPickupGesture() {
+ // GIVEN device is in doze (screen blank, but running doze sensors)
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+ // WHEN the pick up gesture is triggered and keyguard isn't occluded
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+ // THEN wakeup
+ verify(mMachine).wakeUp();
+ }
+
+ @Test
+ public void testPickupGestureDroppedKeyguardOccluded() {
+ // GIVEN device is in doze (screen blank, but running doze sensors)
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+ // WHEN the pick up gesture is triggered and keyguard IS occluded
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+ // THEN never wakeup
+ verify(mMachine, never()).wakeUp();
+ }
+
+ @Test
public void testOnSensor_Fingerprint() {
// GIVEN dozing state
when(mMachine.getState()).thenReturn(DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d279bbb..3e9fbcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
@@ -132,7 +133,8 @@
/* component info */ new ArrayList<>(),
/* sensorType */ 3,
/* resetLockoutRequiresHwToken */ false,
- (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
@@ -165,7 +167,8 @@
/* component info */ new ArrayList<>(),
/* sensorType */ 3,
/* resetLockoutRequiresHwToken */ false,
- (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
new file mode 100644
index 0000000..bc86ef9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet;
+
+import android.app.WallpaperColors;
+import android.graphics.Color;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ColorSchemeTest extends SysuiTestCase {
+ @Test
+ public void testFilterTransparency() {
+ ColorScheme colorScheme = new ColorScheme(Color.TRANSPARENT, false /* darkTheme */);
+ Assert.assertEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testDontFilterOpaque() {
+ ColorScheme colorScheme = new ColorScheme(0xFFFF0000, false /* darkTheme */);
+ Assert.assertNotEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testUniqueColors() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
+ Color.valueOf(0xffaec00a), Color.valueOf(0xffaec00a));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a));
+ }
+
+
+ @Test
+ public void testFiltersInvalidColors() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff5e7ea2),
+ Color.valueOf(0xff5e7ea2), Color.valueOf(0xff000000));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xff5e7ea2));
+ }
+
+ @Test
+ public void testInvalidColorBecomesGBlue() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff000000), null,
+ null);
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xFF1b6ef3));
+ }
+
+ @Test
+ public void testDontFilterRRGGBB() {
+ ColorScheme colorScheme = new ColorScheme(0xFF0000, false /* darkTheme */);
+ Assert.assertEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFFFF0000, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testNoPopulationSignal() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
+ Color.valueOf(0xffbe0000), Color.valueOf(0xffcc040f));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a, 0xffbe0000, 0xffcc040f));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 03248f7..511848d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -22,11 +22,13 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
+import android.os.Process.SYSTEM_UID
import android.os.UserHandle
import android.permission.PermGroupUsage
import android.permission.PermissionManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpsController
import com.android.systemui.plugins.ActivityStarter
@@ -53,6 +55,7 @@
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -96,6 +99,8 @@
private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
@Captor
private lateinit var intentCaptor: ArgumentCaptor<Intent>
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private val uiExecutor = FakeExecutor(FakeSystemClock())
@@ -136,6 +141,7 @@
privacyLogger,
keyguardStateController,
appOpsController,
+ uiEventLogger,
dialogProvider
)
}
@@ -550,6 +556,49 @@
verify(dialog, never()).dismiss()
}
+ @Test
+ fun testCallOnSecondaryUser() {
+ // Calls happen in
+ val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true)
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ `when`(userTracker.userProfiles).thenReturn(listOf(
+ UserInfo(ENT_USER_ID, "", 0)
+ ))
+
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ verify(dialog).show()
+ }
+
+ @Test
+ fun testStartActivityLogs() {
+ val usage = createMockPermGroupUsage()
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ USER_ID, TEST_PACKAGE_NAME)
+ }
+
+ @Test
+ fun testDismissedDialogLogs() {
+ val usage = createMockPermGroupUsage()
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ verify(dialog).addOnDismissListener(capture(dialogDismissedCaptor))
+
+ dialogDismissedCaptor.value.onDialogDismissed()
+
+ controller.dismissDialog()
+
+ verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
+ }
+
private fun exhaustExecutors() {
FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 1ed34d9..c3d60d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -100,6 +101,8 @@
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
+ @Mock
+ private FeatureFlags mFeatureFlags;
public QSFragmentTest() {
super(QSFragment.class);
@@ -145,7 +148,7 @@
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder);
+ mTileServiceRequestControllerBuilder, mFeatureFlags);
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 1a87975..a21f488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.settings.brightness.ToggleSlider;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.animation.DisappearParameters;
@@ -94,6 +95,8 @@
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ CommandQueue mCommandQueue;
FalsingManagerFake mFalsingManager = new FalsingManagerFake();
private QSPanelController mController;
@@ -121,7 +124,7 @@
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFalsingManager
+ mFalsingManager, mCommandQueue
);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index c746bca..aba043b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -50,6 +50,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
@@ -126,6 +127,8 @@
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
+ @Mock
+ private FeatureFlags mFeatureFlags;
private Handler mHandler;
private TestableLooper mLooper;
@@ -145,8 +148,10 @@
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder);
+ mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
+ mFeatureFlags);
setUpTileFactory();
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
}
private void setUpTileFactory() {
@@ -174,13 +179,13 @@
@Test
public void testLoadTileSpecs_emptySetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@Test
public void testLoadTileSpecs_nullSetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@@ -194,6 +199,55 @@
}
@Test
+ public void testRemoveWifiAndCellularWithoutInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
+
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveWifiAndCellularWithInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveWifiWithoutInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveCellWithInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testNoWifiNoCellularNoInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ }
+
+ @Test
public void testSpecWithInvalidDoesNotUseDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
@@ -326,7 +380,7 @@
@Test
public void testLoadTileSpec_repeated() {
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags);
assertEquals(2, specs.size());
assertEquals("spec1", specs.get(0));
@@ -337,7 +391,7 @@
public void testLoadTileSpec_repeatedInDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -348,7 +402,7 @@
public void testLoadTileSpec_repeatedDefaultAndSetting() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -387,11 +441,12 @@
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder) {
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
+ FeatureFlags featureFlags) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister, tileServiceRequestControllerBuilder);
+ customTileStatePersister, tileServiceRequestControllerBuilder, featureFlags);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 126b332..a1b7210 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -232,7 +232,7 @@
MobileDataIndicators indicators = new MobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
- 0, 0, true, true, "", "", "", true, 0, true, true);
+ 0, 0, true, true, "", "", "", 0, true, true);
mSignalCallback.setMobileDataIndicators(indicators);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 4efcc5c..018806e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -53,6 +53,7 @@
import com.android.internal.logging.InstanceId;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -108,6 +109,8 @@
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private FeatureFlags mFeatureFlags;
@Captor
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
@@ -133,12 +136,12 @@
}
}
).when(mQSTileHost).createTile(anyString());
-
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
FakeSystemClock clock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(clock);
mBgExecutor = new FakeExecutor(clock);
mTileQueryHelper = new TileQueryHelper(
- mContext, mUserTracker, mMainExecutor, mBgExecutor);
+ mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags);
mTileQueryHelper.setListener(mListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index e027ab7..a803653 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -102,6 +103,8 @@
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
+ @Mock
+ private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -128,7 +131,8 @@
mUserTracker,
mSecureSettings,
mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder);
+ mTileServiceRequestControllerBuilder,
+ mFeatureFlags);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index baddacc..18f48a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -444,6 +444,47 @@
verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
}
+ @Test
+ public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mWifiEntry1);
+ mAccessPoints.add(mWifiEntry2);
+ mAccessPoints.add(mWifiEntry3);
+ mAccessPoints.add(mWifiEntry4);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ mWifiEntries.add(mWifiEntry2);
+ mWifiEntries.add(mWifiEntry3);
+ mWifiEntries.add(mWifiEntry4);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+ // If the Ethernet exists, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.mHasEthernet = true;
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry4);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+
+ // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry3);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index fa9c053..c42b64a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -67,6 +67,7 @@
private InternetDialog mInternetDialog;
private View mDialogView;
private View mSubTitle;
+ private LinearLayout mEthernet;
private LinearLayout mMobileDataToggle;
private LinearLayout mWifiToggle;
private LinearLayout mConnectedWifi;
@@ -97,6 +98,7 @@
mDialogView = mInternetDialog.mDialogView;
mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+ mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
@@ -139,6 +141,46 @@
}
@Test
+ public void updateDialog_apmOffAndHasEthernet_showEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndHasEthernet_showEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDialogController.hasEthernet()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mInternetDialogController.hasEthernet()).thenReturn(false);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void updateDialog_withApmOn_mobileDataLayoutGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
index ebc6f2a..6a68b71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
@@ -157,6 +157,21 @@
assertThat(view.translationY).isWithin(0.01f).of(3.75f)
}
+ @Test
+ fun testUpdateViewPositions_viewOnTheLeftAndMovedToTheRight_viewTranslatedToTheLeft() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+ animator.onTransitionProgress(0.5f)
+ view.updateMock(x = 80) // view moved from the left side to the right
+
+ animator.updateViewPositions()
+
+ // Negative translationX -> translated to the left
+ assertThat(view.translationX).isWithin(0.1f).of(-5.25f)
+ }
+
private fun createView(
x: Int = 0,
y: Int = 0,
@@ -176,7 +191,30 @@
whenever(view.width).thenReturn(width)
whenever(view.height).thenReturn(height)
- return view.apply {
+ view.updateMock(x, y, width, height, translationX, translationY)
+
+ return view
+ }
+
+ private fun View.updateMock(
+ x: Int = 0,
+ y: Int = 0,
+ width: Int = 10,
+ height: Int = 10,
+ translationX: Float = 0f,
+ translationY: Float = 0f
+ ) {
+ doAnswer {
+ val location = (it.arguments[0] as IntArray)
+ location[0] = x
+ location[1] = y
+ Unit
+ }.`when`(this).getLocationOnScreen(any())
+
+ whenever(this.width).thenReturn(width)
+ whenever(this.height).thenReturn(height)
+
+ this.apply {
setTranslationX(translationX)
setTranslationY(translationY)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
new file mode 100644
index 0000000..096efad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+
+@SmallTest
+class DisableFlagsLoggerTest : SysuiTestCase() {
+ private val disable1Flags = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'b'),
+ DisableFlagsLogger.DisableFlag(0b001, 'C', 'c'),
+ )
+ private val disable2Flags = listOf(
+ DisableFlagsLogger.DisableFlag(0b10, 'M', 'm'),
+ DisableFlagsLogger.DisableFlag(0b01, 'N', 'n'),
+ )
+
+ private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags)
+
+ @Test
+ fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() {
+ val state = DisableFlagsLogger.DisableState(
+ 0b111, // ABC
+ 0b01 // mN
+ )
+
+ val result = disableFlagsLogger.getDisableFlagsString(state, state)
+
+ assertThat(result).contains("Old: ABC.mN")
+ assertThat(result).contains("New: ABC.mN")
+ assertThat(result).doesNotContain("(")
+ assertThat(result).doesNotContain(")")
+ }
+
+ @Test
+ fun getDisableFlagsString_oldAndNewDifferent_statesAndDiffLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(
+ 0b111, // ABC
+ 0b01, // mN
+ ),
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b10 // Mn
+ )
+ )
+
+ assertThat(result).contains("Old: ABC.mN")
+ assertThat(result).contains("New: abC.Mn")
+ assertThat(result).contains("(ab.Mn)")
+ }
+
+ @Test
+ fun getDisableFlagsString_onlyDisable2Different_diffLoggedCorrectly() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b01, // mN
+ ),
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b00 // mn
+ )
+ )
+
+ assertThat(result).contains("(.n)")
+ }
+
+ @Test
+ fun getDisableFlagsString_nullLocalModification_localModNotLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(0, 0),
+ DisableFlagsLogger.DisableState(1, 1),
+ newAfterLocalModification = null
+ )
+
+ assertThat(result).doesNotContain("local modification")
+ }
+
+ @Test
+ fun getDisableFlagsString_newAfterLocalModificationSameAsNew_localModNotLogged() {
+ val newState = DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b10 // mn
+ )
+
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(0, 0), newState, newState
+ )
+
+ assertThat(result).doesNotContain("local modification")
+ }
+
+ @Test
+ fun getDisableFlagsString_newAfterLocalModificationDifferent_localModAndDiffLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ old = DisableFlagsLogger.DisableState(0, 0),
+ new = DisableFlagsLogger.DisableState(
+ 0b000, // abc
+ 0b00 // mn
+ ),
+ newAfterLocalModification = DisableFlagsLogger.DisableState(
+ 0b100, // Abc
+ 0b10 // Mn
+ )
+ )
+
+ assertThat(result).contains("local modification: Abc.Mn (A.M)")
+ }
+
+ @Test
+ fun constructor_defaultDisableFlags_noException() {
+ // Just creating the logger with the default params will trigger the exception if there
+ // is one.
+ DisableFlagsLogger()
+ }
+
+ @Test
+ fun constructor_disable1_FlagIsSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'),
+ ),
+ listOf()
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable1_FlagNotSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'),
+ ),
+ listOf()
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable2_FlagIsSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(),
+ disable2FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'),
+ ),
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable2_FlagNotSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(),
+ disable2FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'),
+ ),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 60f0b68..4276f7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -274,6 +274,26 @@
}
@Test
+ public void onBiometricAuthenticated_onLockScreen() {
+ // GIVEN not dozing
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+
+ // WHEN we want to unlock collapse
+ mBiometricUnlockController.startWakeAndUnlock(
+ BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
+
+ // THEN we collpase the panels and notify authenticated
+ verify(mShadeController).animateCollapsePanels(
+ /* flags */ anyInt(),
+ /* force */ eq(true),
+ /* delayed */ eq(false),
+ /* speedUpFactor */ anyFloat()
+ );
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(
+ /* strongAuth */ eq(false));
+ }
+
+ @Test
public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() {
reset(mUpdateMonitor);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
new file mode 100644
index 0000000..f3136c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.statusbar.DisableFlagsLogger
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
+
+@SmallTest
+class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() {
+
+ private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ private val disableFlagsLogger = DisableFlagsLogger(
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+ )
+ private val logger = CollapsedStatusBarFragmentLogger(buffer, disableFlagsLogger)
+
+ @Test
+ fun logToBuffer_bufferHasStates() {
+ val state = DisableFlagsLogger.DisableState(0, 1)
+
+ logger.logDisableFlagChange(state, state)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+ val expectedLogString = disableFlagsLogger.getDisableFlagsString(state, state)
+
+ assertThat(actualString).contains(expectedLogString)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index e159cae..48cdd42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -39,8 +39,11 @@
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogcatEchoTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -265,6 +268,10 @@
mStatusBarStateController,
() -> Optional.of(mStatusBar),
mCommandQueue,
+ new CollapsedStatusBarFragmentLogger(
+ new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
+ new DisableFlagsLogger()
+ ),
mOperatorNameViewControllerFactory);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index d63730d..52a5e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -16,21 +16,39 @@
package com.android.systemui.statusbar.phone
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+ private val stateChangeListener = TestStateChangedListener()
+
@Mock
private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var panelViewController: PanelViewController
+ @Mock
+ private lateinit var panelView: ViewGroup
+ @Mock
+ private lateinit var scrimController: ScrimController
+
+ @Mock
+ private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -38,8 +56,23 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- view = PhoneStatusBarView(mContext, null)
- controller = PhoneStatusBarViewController(view, commandQueue)
+ `when`(panelViewController.view).thenReturn(panelView)
+
+ // create the view on main thread as it requires main looper
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val parent = FrameLayout(mContext) // add parent to keep layout params
+ view = LayoutInflater.from(mContext)
+ .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+ view.setPanel(panelViewController)
+ view.setScrimController(scrimController)
+ }
+
+ controller = PhoneStatusBarViewController(
+ view,
+ commandQueue,
+ null,
+ stateChangeListener
+ )
}
@Test
@@ -56,4 +89,32 @@
assertThat(providerUsed).isTrue()
}
+
+ @Test
+ fun constructor_moveFromCenterAnimationIsNotNull_moveFromCenterAnimationInitialized() {
+ controller = PhoneStatusBarViewController(
+ view, commandQueue, moveFromCenterAnimation, stateChangeListener
+ )
+
+ verify(moveFromCenterAnimation).init(any(), any())
+ }
+
+ @Test
+ fun constructor_setsExpansionStateChangedListenerOnView() {
+ assertThat(stateChangeListener.stateChangeCalled).isFalse()
+
+ // If the constructor correctly set the listener, then it should be used when
+ // [PhoneStatusBarView.panelExpansionChanged] is called.
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(stateChangeListener.stateChangeCalled).isTrue()
+ }
+
+ private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ var stateChangeCalled: Boolean = false
+
+ override fun onPanelExpansionStateChanged() {
+ stateChangeCalled = true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 49ab6eb..aee9f12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -16,20 +16,38 @@
package com.android.systemui.statusbar.phone
+import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
@SmallTest
class PhoneStatusBarViewTest : SysuiTestCase() {
+ @Mock
+ private lateinit var panelViewController: PanelViewController
+ @Mock
+ private lateinit var panelView: ViewGroup
+ @Mock
+ private lateinit var scrimController: ScrimController
+
private lateinit var view: PhoneStatusBarView
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ // TODO(b/197137564): Setting up a panel view and its controller feels unnecessary when
+ // testing just [PhoneStatusBarView].
+ `when`(panelViewController.view).thenReturn(panelView)
+
view = PhoneStatusBarView(mContext, null)
+ view.setPanel(panelViewController)
+ view.setScrimController(scrimController)
}
@Test
@@ -51,4 +69,48 @@
view.panelEnabled()
// No assert needed, just testing no crash
}
+
+ @Test
+ fun panelExpansionChanged_fracZero_stateChangeListenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(listener.stateChangeCalled).isTrue()
+ }
+
+ @Test
+ fun panelExpansionChanged_fracOne_stateChangeListenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(1f, false)
+
+ assertThat(listener.stateChangeCalled).isTrue()
+ }
+
+ @Test
+ fun panelExpansionChanged_fracHalf_stateChangeListenerNotNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(0.5f, false)
+
+ assertThat(listener.stateChangeCalled).isFalse()
+ }
+
+ @Test
+ fun panelExpansionChanged_noStateChangeListener_noCrash() {
+ view.panelExpansionChanged(1f, false)
+ // No assert needed, just testing no crash
+ }
+
+ private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ var stateChangeCalled: Boolean = false
+
+ override fun onPanelExpansionStateChanged() {
+ stateChangeCalled = true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
index 52538c7..8555306 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -112,6 +113,7 @@
mVibratorHelper,
Optional.of(mVibrator),
mLightBarController,
+ new DisableFlagsLogger(),
DEFAULT_DISPLAY);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b23414b..751bc81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -233,6 +233,7 @@
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
+ @Mock private CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
@Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
@Mock private StatusBarComponent mStatusBarComponent;
@Mock private PluginManager mPluginManager;
@@ -254,6 +255,7 @@
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
@Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
@Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
+ @Mock private Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimationLazy;
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
@@ -403,6 +405,7 @@
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
+ mCollapsedStatusBarFragmentLogger,
mStatusBarComponentFactory,
mPluginManager,
Optional.of(mLegacySplitScreen),
@@ -428,6 +431,7 @@
mBrightnessSliderFactory,
mUnfoldTransitionConfig,
mUnfoldLightRevealOverlayAnimationLazy,
+ mMoveFromCenterAnimationLazy,
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 2418243..3c55df9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -124,7 +124,7 @@
boolean roaming = true;
MobileDataIndicators indicators = new MobileDataIndicators(
status, qs, type, qsType, in, out, typeDescription,
- typeDescriptionHtml, description, wide, subId, roaming, true);
+ typeDescriptionHtml, description, subId, roaming, true);
mHandler.setMobileDataIndicators(indicators);
waitForCallbacks();
@@ -141,8 +141,7 @@
assertEquals(out, expected.activityOut);
assertEquals(typeDescription, expected.typeContentDescription);
assertEquals(typeDescriptionHtml, expected.typeContentDescriptionHtml);
- assertEquals(description, expected.description);
- assertEquals(wide, expected.isWide);
+ assertEquals(description, expected.qsDescription);
assertEquals(subId, expected.subId);
assertTrue(expected.roaming);
assertTrue(expected.showTriangle);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index cd911cc..4c282bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -167,7 +167,7 @@
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
.isEqualTo(new OverlayIdentifier("ffff0000"));
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(new OverlayIdentifier("ff0000ff"));
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
// Should not ask again if changed to same value
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 44a4997..20ad9e1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -208,7 +208,7 @@
private ScanCallback mBleScanCallback = new BleScanCallback();
private AssociationRequest mRequest;
private String mCallingPackage;
- private AndroidFuture<Association> mOngoingDeviceDiscovery;
+ private AndroidFuture<?> mOngoingDeviceDiscovery;
private PermissionControllerManager mPermissionControllerManager;
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
@@ -383,7 +383,7 @@
Slog.d(LOG_TAG, "cleanup(); discovery = "
+ mOngoingDeviceDiscovery + ", request = " + mRequest);
synchronized (mLock) {
- AndroidFuture<Association> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
+ AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
ongoingDeviceDiscovery.cancel(true);
}
@@ -458,13 +458,16 @@
return mServiceConnectors.forUser(userId).postAsync(service -> {
Slog.d(LOG_TAG, "Connected to CDM service; starting discovery for " + request);
- AndroidFuture<Association> future = new AndroidFuture<>();
+ AndroidFuture<String> future = new AndroidFuture<>();
service.startDiscovery(request, callingPackage, callback, future);
return future;
}).cancelTimeout();
- }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> {
+ }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
if (err == null) {
+ Association association = new Association(userId, deviceAddress, callingPackage,
+ mRequest.getDeviceProfile(), false,
+ System.currentTimeMillis());
addAssociation(association, userId);
} else {
Slog.e(LOG_TAG, "Failed to discover device(s)", err);
@@ -575,7 +578,7 @@
return PendingIntent.getActivityAsUser(getContext(),
0 /* request code */,
NotificationAccessConfirmationActivityContract.launcherIntent(
- userId, component, packageTitle),
+ getContext(), userId, component, packageTitle),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_CANCEL_CURRENT,
null /* options */,
diff --git a/services/core/java/android/content/pm/TEST_MAPPING b/services/core/java/android/content/pm/TEST_MAPPING
new file mode 100644
index 0000000..54303cb
--- /dev/null
+++ b/services/core/java/android/content/pm/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/core/java/android/content/pm"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index 5bd27c4..923db4f 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -16,17 +16,19 @@
package com.android.server;
import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.os.FileUtils;
import android.os.UEventObserver;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
-
+import com.android.internal.annotations.GuardedBy;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
-import java.util.regex.Pattern;
/**
* A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code
@@ -87,41 +89,149 @@
/** An External Connection to watch. */
public static final class ExtconInfo {
- private static final String TAG = "ExtconInfo";
+ /* Copied from drivers/extcon/extcon.c */
- /** Returns a new list of all external connections whose name matches {@code regex}. */
- public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
- if (!extconExists()) {
- return new ArrayList<>(0); // Always return a new list.
+ /* USB external connector */
+ public static final String EXTCON_USB = "USB";
+ public static final String EXTCON_USB_HOST = "USB-HOST";
+
+ /* Charger external connector */
+ public static final String EXTCON_TA = "TA";
+ public static final String EXTCON_FAST_CHARGER = "FAST-CHARGER";
+ public static final String EXTCON_SLOW_CHARGER = "SLOW-CHARGER";
+ public static final String EXTCON_CHARGE_DOWNSTREAM = "CHARGE-DOWNSTREAM";
+
+ /* Audio/Video external connector */
+ public static final String EXTCON_LINE_IN = "LINE-IN";
+ public static final String EXTCON_LINE_OUT = "LINE-OUT";
+ public static final String EXTCON_MICROPHONE = "MICROPHONE";
+ public static final String EXTCON_HEADPHONE = "HEADPHONE";
+
+ public static final String EXTCON_HDMI = "HDMI";
+ public static final String EXTCON_MHL = "MHL";
+ public static final String EXTCON_DVI = "DVI";
+ public static final String EXTCON_VGA = "VGA";
+ public static final String EXTCON_SPDIF_IN = "SPDIF-IN";
+ public static final String EXTCON_SPDIF_OUT = "SPDIF-OUT";
+ public static final String EXTCON_VIDEO_IN = "VIDEO-IN";
+ public static final String EXTCON_VIDEO_OUT = "VIDEO-OUT";
+
+ /* Etc external connector */
+ public static final String EXTCON_DOCK = "DOCK";
+ public static final String EXTCON_JIG = "JIG";
+ public static final String EXTCON_MECHANICAL = "MECHANICAL";
+
+ @StringDef({
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_TA,
+ EXTCON_FAST_CHARGER,
+ EXTCON_SLOW_CHARGER,
+ EXTCON_CHARGE_DOWNSTREAM,
+ EXTCON_LINE_IN,
+ EXTCON_LINE_OUT,
+ EXTCON_MICROPHONE,
+ EXTCON_HEADPHONE,
+ EXTCON_HDMI,
+ EXTCON_MHL,
+ EXTCON_DVI,
+ EXTCON_VGA,
+ EXTCON_SPDIF_IN,
+ EXTCON_SPDIF_OUT,
+ EXTCON_VIDEO_IN,
+ EXTCON_VIDEO_OUT,
+ EXTCON_DOCK,
+ EXTCON_JIG,
+ EXTCON_MECHANICAL,
+ })
+
+ public @interface ExtconDeviceType {}
+
+ private static final Object sLock = new Object();
+ private static ExtconInfo[] sExtconInfos = null;
+
+ private final String mName;
+ private final @ExtconDeviceType HashSet<String> mDeviceTypes = new HashSet<>();
+
+ @GuardedBy("sLock")
+ private static void initExtconInfos() {
+ if (sExtconInfos != null) {
+ return;
}
- Pattern p = regex == null ? null : Pattern.compile(regex);
+
File file = new File("/sys/class/extcon");
File[] files = file.listFiles();
if (files == null) {
- Slog.wtf(TAG, file + " exists " + file.exists() + " isDir " + file.isDirectory()
- + " but listFiles returns null. "
- + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
- return new ArrayList<>(0); // Always return a new list.
+ Slog.w(TAG,
+ file + " exists " + file.exists() + " isDir " + file.isDirectory()
+ + " but listFiles returns null."
+ + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
+ sExtconInfos = new ExtconInfo[0];
} else {
- ArrayList list = new ArrayList(files.length);
+ List<ExtconInfo> list = new ArrayList<>(files.length);
for (File f : files) {
- String name = f.getName();
- if (p == null || p.matcher(name).matches()) {
- ExtconInfo uei = new ExtconInfo(name);
- list.add(uei);
- if (LOG) Slog.d(TAG, name + " matches " + regex);
- } else {
- if (LOG) Slog.d(TAG, name + " does not match " + regex);
- }
+ list.add(new ExtconInfo(f.getName()));
}
- return list;
+ sExtconInfos = list.toArray(new ExtconInfo[0]);
}
}
- private final String mName;
+ /**
+ * Returns a new list of all external connections for the types given.
+ */
+ public static List<ExtconInfo> getExtconInfoForTypes(
+ @ExtconDeviceType String[] extconTypes) {
+ synchronized (sLock) {
+ initExtconInfos();
+ }
- public ExtconInfo(String name) {
- mName = name;
+ List<ExtconInfo> extcons = new ArrayList<ExtconInfo>();
+ for (ExtconInfo extcon : sExtconInfos) {
+ for (String type : extconTypes) {
+ if (extcon.hasCableType(type)) {
+ extcons.add(extcon);
+ break;
+ }
+ }
+ }
+
+ return extcons;
+ }
+
+ /** True if the given type is supported */
+ public boolean hasCableType(@ExtconDeviceType String type) {
+ return mDeviceTypes.contains(type);
+ }
+
+ private ExtconInfo(String extconName) {
+ mName = extconName;
+
+ // Retrieve device types from /sys/class/extcon/extcon[X]/cable.[Y]/name
+ File[] cableDirs = FileUtils.listFilesOrEmpty(new File("/sys/class/extcon", mName),
+ (dir, cable) -> cable.startsWith("cable."));
+ if (cableDirs.length == 0) {
+ Slog.d(TAG,
+ "Unable to list cables in /sys/class/extcon/" + mName + ". "
+ + SELINUX_POLICIES_NEED_TO_BE_CHANGED);
+ }
+
+ for (File cableDir : cableDirs) {
+ String cableCanonicalPath = null;
+ try {
+ cableCanonicalPath = cableDir.getCanonicalPath();
+ String name = FileUtils.readTextFile(new File(cableDir, "name"), 0, null);
+ name = name.replace("\n", "").replace("\r", "");
+ if (LOG) {
+ Slog.v(TAG, "Add extcon cable " + cableCanonicalPath);
+ }
+ mDeviceTypes.add(name);
+ } catch (IOException ex) {
+ Slog.w(TAG,
+ "Unable to read " + cableCanonicalPath + "/name. "
+ + SELINUX_POLICIES_NEED_TO_BE_CHANGED,
+ ex);
+ }
+ }
}
/** The name of the external connection */
@@ -139,7 +249,7 @@
@Nullable
public String getDevicePath() {
try {
- String extconPath = String.format(Locale.US, "/sys/class/extcon/%s", mName);
+ String extconPath = TextUtils.formatSimple("/sys/class/extcon/%s", mName);
File devPath = new File(extconPath);
if (devPath.exists()) {
String canonicalPath = devPath.getCanonicalPath();
@@ -155,16 +265,10 @@
/** The path to the state file */
public String getStatePath() {
- return String.format(Locale.US, "/sys/class/extcon/%s/state", mName);
+ return TextUtils.formatSimple("/sys/class/extcon/%s/state", mName);
}
}
- /** Does the {@code /sys/class/extcon/<name>} directory exist */
- public static boolean namedExtconDirExists(String name) {
- File extconDir = new File("/sys/class/extcon/" + name);
- return extconDir.exists() && extconDir.isDirectory();
- }
-
/** Does the {@code /sys/class/extcon} directory exist */
public static boolean extconExists() {
File extconDir = new File("/sys/class/extcon");
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 7fa93c0..b271d7e 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -490,8 +490,12 @@
private final List<ExtconInfo> mExtconInfos;
WiredAccessoryExtconObserver() {
- mExtconInfos = ExtconInfo.getExtconInfos(".*audio.*");
-
+ mExtconInfos = ExtconInfo.getExtconInfoForTypes(new String[] {
+ ExtconInfo.EXTCON_HEADPHONE,
+ ExtconInfo.EXTCON_MICROPHONE,
+ ExtconInfo.EXTCON_HDMI,
+ ExtconInfo.EXTCON_LINE_OUT,
+ });
}
private void init() {
@@ -521,15 +525,23 @@
@Override
public Pair<Integer, Integer> parseState(ExtconInfo extconInfo, String status) {
if (LOG) Slog.v(TAG, "status " + status);
- int []maskAndState = {0,0};
+ int[] maskAndState = {0, 0};
// extcon event state changes from kernel4.9
// new state will be like STATE=MICROPHONE=1\nHEADPHONE=0
- updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, "HEADPHONE") ;
- updateBit(maskAndState, BIT_HEADSET, status,"MICROPHONE") ;
- updateBit(maskAndState, BIT_HDMI_AUDIO, status,"HDMI") ;
- updateBit(maskAndState, BIT_LINEOUT, status,"LINE-OUT") ;
+ if (extconInfo.hasCableType(ExtconInfo.EXTCON_HEADPHONE)) {
+ updateBit(maskAndState, BIT_HEADSET_NO_MIC, status, ExtconInfo.EXTCON_HEADPHONE);
+ }
+ if (extconInfo.hasCableType(ExtconInfo.EXTCON_MICROPHONE)) {
+ updateBit(maskAndState, BIT_HEADSET, status, ExtconInfo.EXTCON_MICROPHONE);
+ }
+ if (extconInfo.hasCableType(ExtconInfo.EXTCON_HDMI)) {
+ updateBit(maskAndState, BIT_HDMI_AUDIO, status, ExtconInfo.EXTCON_HDMI);
+ }
+ if (extconInfo.hasCableType(ExtconInfo.EXTCON_LINE_OUT)) {
+ updateBit(maskAndState, BIT_LINEOUT, status, ExtconInfo.EXTCON_LINE_OUT);
+ }
if (LOG) Slog.v(TAG, "mask " + maskAndState[0] + " state " + maskAndState[1]);
- return Pair.create(maskAndState[0],maskAndState[1]);
+ return Pair.create(maskAndState[0], maskAndState[1]);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 81a3d53..2b30943 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -20,9 +20,7 @@
import static android.Manifest.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -2898,7 +2896,6 @@
}
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
false);
- logBindServiceFromCachedState(callingPackage, callingUid, service);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -2963,30 +2960,6 @@
return 1;
}
- /**
- * Log a WTF message if the bindService is called by a process from a cached proc state.
- * This WTF log is to debug background restriction, it will be removed in before final release.
- * @param callerPackage the caller's package name.
- * @param callingUid the caller's UID.
- * @param intent the service's intent.
- */
- private void logBindServiceFromCachedState(String callerPackage, int callingUid,
- Intent intent) {
- final int callerUidState = mAm.getUidStateLocked(callingUid);
- if (callerUidState == PROCESS_STATE_NONEXISTENT
- || callerUidState < PROCESS_STATE_CACHED_ACTIVITY) {
- return;
- }
- final String msg = "bindService from cached state "
- + "[callerPackage:" + callerPackage
- + "; callingUid:" + callingUid
- + "; uidState:" + ProcessList.makeProcStateString(callerUidState)
- + "; intent:" + intent
- + ";]";
- Slog.wtfQuiet(TAG, msg);
- Slog.i(TAG, msg);
- }
-
private void maybeLogBindCrossProfileService(
int userId, String callingPackage, int callingUid) {
if (UserHandle.isCore(callingUid)) {
@@ -5248,7 +5221,9 @@
sr.setProcess(null, null, 0, null);
sr.isolatedProc = null;
sr.executeNesting = 0;
- sr.forceClearTracker();
+ synchronized (mAm.mProcessStats.mLock) {
+ sr.forceClearTracker();
+ }
if (mDestroyingServices.remove(sr)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
}
@@ -5398,7 +5373,9 @@
i--;
ServiceRecord sr = mDestroyingServices.get(i);
if (sr.app == app) {
- sr.forceClearTracker();
+ synchronized (mAm.mProcessStats.mLock) {
+ sr.forceClearTracker();
+ }
mDestroyingServices.remove(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index db49b56..bcb1be3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -123,7 +123,6 @@
static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE = "kill_bg_restricted_cached_idle";
static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME =
"kill_bg_restricted_cached_idle_settle_time";
- static final String KEY_ENABLE_COMPONENT_ALIAS = "enable_experimental_component_alias";
static final String KEY_COMPONENT_ALIAS_OVERRIDES = "component_alias_overrides";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
@@ -200,7 +199,6 @@
* Whether or not to enable the extra delays to service restarts on memory pressure.
*/
private static final boolean DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = true;
- private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false;
private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = "";
// Flag stored in the DeviceConfig API.
@@ -595,8 +593,6 @@
@GuardedBy("mService")
boolean mEnableExtraServiceRestartDelayOnMemPressure =
DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
- /** Whether to enable "component alias" experimental feature. */
- volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS;
/**
* Defines component aliases. Format
@@ -835,7 +831,6 @@
case KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE:
updateEnableExtraServiceRestartDelayOnMemPressure();
break;
- case KEY_ENABLE_COMPONENT_ALIAS:
case KEY_COMPONENT_ALIAS_OVERRIDES:
updateComponentAliases();
break;
@@ -1274,15 +1269,11 @@
}
private void updateComponentAliases() {
- mEnableComponentAlias = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
- KEY_ENABLE_COMPONENT_ALIAS,
- DEFAULT_ENABLE_COMPONENT_ALIAS);
mComponentAliasOverrides = DeviceConfig.getString(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPONENT_ALIAS_OVERRIDES,
DEFAULT_COMPONENT_ALIAS_OVERRIDES);
- mService.mComponentAliasResolver.update(mEnableComponentAlias, mComponentAliasOverrides);
+ mService.mComponentAliasResolver.update(mComponentAliasOverrides);
}
private void updateProcessKillTimeout() {
@@ -1521,8 +1512,6 @@
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
pw.print("="); pw.println(mFgsAllowOptOut);
- pw.print(" "); pw.print(KEY_ENABLE_COMPONENT_ALIAS);
- pw.print("="); pw.println(mEnableComponentAlias);
pw.print(" "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES);
pw.print("="); pw.println(mComponentAliasOverrides);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5179c0d..bfd6a03 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4838,10 +4838,6 @@
showConsoleNotificationIfActive();
t.traceEnd();
-
- // Load the component aliases.
- mComponentAliasResolver.update(
- mConstants.mEnableComponentAlias, mConstants.mComponentAliasOverrides);
}
private void showConsoleNotificationIfActive() {
@@ -5054,8 +5050,9 @@
}
@Override
- public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
- mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
+ public boolean registerIntentSenderCancelListenerEx(
+ IIntentSender sender, IResultReceiver receiver) {
+ return mPendingIntentController.registerIntentSenderCancelListener(sender, receiver);
}
@Override
@@ -7827,6 +7824,12 @@
t.traceEnd(); // setBinderProxies
t.traceEnd(); // ActivityManagerStartApps
+
+ // Load the component aliases.
+ t.traceBegin("componentAlias");
+ mComponentAliasResolver.onSystemReady(mConstants.mComponentAliasOverrides);
+ t.traceEnd(); // componentAlias
+
t.traceEnd(); // PhaseActivityManagerReady
}
}
@@ -12682,8 +12685,7 @@
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
- null, null, -1, -1, PROCESS_STATE_NONEXISTENT, false, null, null, null,
- OP_NONE, null, receivers,
+ null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
@@ -12961,7 +12963,6 @@
@Nullable int[] broadcastAllowList) {
intent = new Intent(intent);
- final int callerUidState = getUidStateLocked(realCallingUid);
final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
// Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
if (callerInstantApp) {
@@ -13529,8 +13530,7 @@
}
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
- callerFeatureId, callingPid, callingUid, callerUidState, callerInstantApp,
- resolvedType,
+ callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers,
resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, backgroundActivityStartsToken,
@@ -13628,8 +13628,7 @@
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
- callerFeatureId, callingPid, callingUid, callerUidState, callerInstantApp,
- resolvedType,
+ callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, appOp, brOptions,
receivers, resultTo, resultCode, resultData, resultExtras,
ordered, sticky, false, userId, allowBackgroundActivityStarts,
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index fd64c3c..ed70d2b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
import static android.text.TextUtils.formatSimple;
@@ -1658,9 +1656,6 @@
scheduleBroadcastsLocked();
return;
}
-
- logSendBroadcastFromCachedState(r, component);
-
r.manifestCount++;
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
@@ -1755,29 +1750,6 @@
mPendingBroadcastRecvIndex = recIdx;
}
- /**
- * Log a WTF message if the broadcast is sent by a process from a cached proc state.
- * This WTF log is to debug background restriction, it will be removed in before final release.
- * @param r the BroadcastRecord.
- * @param component the broadcast's resolved ComponentName.
- */
- private void logSendBroadcastFromCachedState(BroadcastRecord r, ComponentName component) {
- if (r.callerUidState == PROCESS_STATE_NONEXISTENT
- || r.callerUidState < PROCESS_STATE_CACHED_ACTIVITY) {
- return;
- }
- final String msg = "sendBroadcast from cached state"
- + "[callerPackage:" + r.callerPackage
- + "; callingUid:" + r.callingUid
- + "; realCallingUid:" + r.callingUid
- + "; uidState:" + ProcessList.makeProcStateString(r.callerUidState)
- + "; intent:" + r.intent
- + "; component:" + component.flattenToShortString()
- + ";]";
- Slog.wtfQuiet(TAG, msg);
- Slog.i(TAG, msg);
- }
-
private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
ComponentName component) {
if (info.activityInfo.attributionTags == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 53445b3..8015596 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -55,7 +55,6 @@
final @Nullable String callerFeatureId; // which feature in the package sent this
final int callingPid; // the pid of who sent this
final int callingUid; // the uid of who sent this
- final int callerUidState; // the sender's UID state when sent this.
final boolean callerInstantApp; // caller is an Instant App?
final boolean ordered; // serialize the send to receivers?
final boolean sticky; // originated from existing sticky data?
@@ -245,7 +244,7 @@
BroadcastRecord(BroadcastQueue _queue,
Intent _intent, ProcessRecord _callerApp, String _callerPackage,
@Nullable String _callerFeatureId, int _callingPid, int _callingUid,
- int _callerUidState, boolean _callerInstantApp, String _resolvedType,
+ boolean _callerInstantApp, String _resolvedType,
String[] _requiredPermissions, String[] _excludedPermissions, int _appOp,
BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode,
String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky,
@@ -262,7 +261,6 @@
callerFeatureId = _callerFeatureId;
callingPid = _callingPid;
callingUid = _callingUid;
- this.callerUidState = _callerUidState;
callerInstantApp = _callerInstantApp;
resolvedType = _resolvedType;
requiredPermissions = _requiredPermissions;
@@ -300,7 +298,6 @@
callerFeatureId = from.callerFeatureId;
callingPid = from.callingPid;
callingUid = from.callingUid;
- callerUidState = from.callerUidState;
callerInstantApp = from.callerInstantApp;
ordered = from.ordered;
sticky = from.sticky;
@@ -365,8 +362,7 @@
// build a new BroadcastRecord around that single-target list
BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
- callerFeatureId, callingPid, callingUid, callerUidState, callerInstantApp,
- resolvedType,
+ callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, appOp, options, splitReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f32aa22..9da6d52 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -157,6 +157,9 @@
static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
+ // Bitfield values for sync transactions received by frozen binder threads
+ static final int TXNS_PENDING_WHILE_FROZEN = 4;
+
/**
* This thread must be moved to the system background cpuset.
* If that doesn't happen, it's probably going to draw a lot of power.
@@ -611,8 +614,9 @@
* binder for the specificed pid.
*
* @throws RuntimeException in case a flush/freeze operation could not complete successfully.
+ * @return 0 if success, or -EAGAIN indicating there's pending transaction.
*/
- private static native void freezeBinder(int pid, boolean freeze);
+ private static native int freezeBinder(int pid, boolean freeze);
/**
* Retrieves binder freeze info about a process.
@@ -948,7 +952,7 @@
int freezeInfo = getBinderFreezeInfo(pid);
if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName
+ " received sync transactions while frozen, killing");
app.killLocked("Sync transaction while in frozen state",
ApplicationExitInfo.REASON_OTHER,
@@ -956,8 +960,8 @@
processKilled = true;
}
- if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0 && DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName
+ " received async transactions while frozen");
}
} catch (Exception e) {
@@ -1292,7 +1296,9 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_FROZEN_PROCESS_MSG:
- freezeProcess((ProcessRecord) msg.obj);
+ synchronized (mAm) {
+ freezeProcess((ProcessRecord) msg.obj);
+ }
break;
case REPORT_UNFREEZE_MSG:
int pid = msg.arg1;
@@ -1306,6 +1312,15 @@
}
}
+ @GuardedBy({"mAm", "mProcLock"})
+ private void rescheduleFreeze(final ProcessRecord proc, final String reason) {
+ Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid()
+ + " " + proc.processName + " (" + reason + ")");
+ unfreezeAppLSP(proc);
+ freezeAppAsyncLSP(proc);
+ }
+
+ @GuardedBy({"mAm"})
private void freezeProcess(final ProcessRecord proc) {
int pid = proc.getPid(); // Unlocked intentionally
final String name = proc.processName;
@@ -1355,10 +1370,15 @@
return;
}
+ Slog.d(TAG_AM, "freezing " + pid + " " + name);
+
// Freeze binder interface before the process, to flush any
// transactions that might be pending.
try {
- freezeBinder(pid, true);
+ if (freezeBinder(pid, true) != 0) {
+ rescheduleFreeze(proc, "outstanding txns");
+ return;
+ }
} catch (RuntimeException e) {
Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
mFreezeHandler.post(() -> {
@@ -1404,24 +1424,36 @@
try {
// post-check to prevent races
+ int freezeInfo = getBinderFreezeInfo(pid);
+
+ if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
+ synchronized (mProcLock) {
+ rescheduleFreeze(proc, "new pending txns");
+ }
+ return;
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ mFreezeHandler.post(() -> {
+ synchronized (mAm) {
+ proc.killLocked("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
+ }
+ });
+ }
+
+ try {
+ // post-check to prevent races
if (mProcLocksReader.hasFileLocks(pid)) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
}
-
- synchronized (mAm) {
- synchronized (mProcLock) {
- unfreezeAppLSP(proc);
- }
- }
+ unfreezeAppLSP(proc);
}
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
- synchronized (mAm) {
- synchronized (mProcLock) {
- unfreezeAppLSP(proc);
- }
- }
+ unfreezeAppLSP(proc);
}
}
diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java
index cf3e968..00b0e83 100644
--- a/services/core/java/com/android/server/am/ComponentAliasResolver.java
+++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java
@@ -17,15 +17,20 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -36,6 +41,8 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.compat.CompatChange;
+import com.android.server.compat.PlatformCompat;
import java.io.PrintWriter;
import java.util.List;
@@ -59,6 +66,13 @@
private static final String TAG = "ComponentAliasResolver";
private static final boolean DEBUG = true;
+ /**
+ * This flag has to be enabled for the "android" package to use component aliases.
+ */
+ @ChangeId
+ @Disabled
+ public static final long USE_EXPERIMENTAL_COMPONENT_ALIAS = 196254758L;
+
private final Object mLock = new Object();
private final ActivityManagerService mAm;
private final Context mContext;
@@ -72,6 +86,11 @@
@GuardedBy("mLock")
private final ArrayMap<ComponentName, ComponentName> mFromTo = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private PlatformCompat mPlatformCompat;
+
+ private static final String OPT_IN_PROPERTY = "com.android.EXPERIMENTAL_COMPONENT_ALIAS_OPT_IN";
+
private static final String ALIAS_FILTER_ACTION = "android.intent.action.EXPERIMENTAL_IS_ALIAS";
private static final String META_DATA_ALIAS_TARGET = "alias_target";
@@ -114,14 +133,32 @@
}
};
+ private final CompatChange.ChangeListener mCompatChangeListener = (packageName) -> {
+ if (DEBUG) Slog.d(TAG, "USE_EXPERIMENTAL_COMPONENT_ALIAS changed.");
+ BackgroundThread.getHandler().post(this::refresh);
+ };
+
+ /**
+ * Call this on systemRead().
+ */
+ public void onSystemReady(String overrides) {
+ synchronized (mLock) {
+ mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+ Context.PLATFORM_COMPAT_SERVICE);
+ mPlatformCompat.registerListener(USE_EXPERIMENTAL_COMPONENT_ALIAS,
+ mCompatChangeListener);
+ }
+ if (DEBUG) Slog.d(TAG, "Compat listener set.");
+ update(overrides);
+ }
+
/**
* (Re-)loads aliases from <meta-data> and the device config override.
*/
- public void update(boolean enabled, String overrides) {
+ public void update(String overrides) {
synchronized (mLock) {
- if (enabled == mEnabled && Objects.equals(overrides, mOverrideString)) {
- return;
- }
+ final boolean enabled = mPlatformCompat.isChangeEnabledByPackageName(
+ USE_EXPERIMENTAL_COMPONENT_ALIAS, "android", UserHandle.USER_SYSTEM);
if (enabled != mEnabled) {
Slog.i(TAG, (enabled ? "Enabling" : "Disabling") + " component aliases...");
if (enabled) {
@@ -144,7 +181,7 @@
private void refresh() {
synchronized (mLock) {
- refreshLocked();
+ update(mOverrideString);
}
}
@@ -167,18 +204,80 @@
final List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser(
i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM);
- extractAliases(services);
+ extractAliasesLocked(services);
if (DEBUG) Slog.d(TAG, "Scanning receiver aliases...");
final List<ResolveInfo> receivers = mContext.getPackageManager()
.queryBroadcastReceiversAsUser(i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM);
- extractAliases(receivers);
+ extractAliasesLocked(receivers);
// TODO: Scan for other component types as well.
}
- private void extractAliases(List<ResolveInfo> components) {
+ /**
+ * Make sure a given package is opted into component alias, by having a
+ * "com.android.EXPERIMENTAL_COMPONENT_ALIAS_OPT_IN" property set to true in the manifest.
+ *
+ * The implementation isn't optimized -- in every call we scan the package's properties,
+ * even thought we're likely going to call it with the same packages multiple times.
+ * But that's okay since this feature is experimental, and this code path won't be called
+ * until explicitly enabled.
+ */
+ @GuardedBy("mLock")
+ private boolean isEnabledForPackageLocked(String packageName) {
+ boolean enabled = false;
+ try {
+ final Property p = mContext.getPackageManager().getProperty(
+ OPT_IN_PROPERTY, packageName);
+ enabled = p.getBoolean();
+ } catch (NameNotFoundException e) {
+ }
+ if (!enabled) {
+ Slog.w(TAG, "USE_EXPERIMENTAL_COMPONENT_ALIAS not enabled for " + packageName);
+ }
+ return enabled;
+ }
+
+ /**
+ * Make sure an alias and its target are the same package, or, the target is in a "sub" package.
+ */
+ private static boolean validateAlias(ComponentName from, ComponentName to) {
+ final String fromPackage = from.getPackageName();
+ final String toPackage = to.getPackageName();
+
+ if (Objects.equals(fromPackage, toPackage)) { // Same package?
+ return true;
+ }
+ if (toPackage.startsWith(fromPackage + ".")) { // Prefix?
+ return true;
+ }
+ Slog.w(TAG, "Invalid alias: "
+ + from.flattenToShortString() + " -> " + to.flattenToShortString());
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ private void validateAndAddAliasLocked(ComponentName from, ComponentName to) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "" + from.flattenToShortString() + " -> " + to.flattenToShortString());
+ }
+ if (!validateAlias(from, to)) {
+ return;
+ }
+
+ // Make sure both packages have
+ if (!isEnabledForPackageLocked(from.getPackageName())
+ || !isEnabledForPackageLocked(to.getPackageName())) {
+ return;
+ }
+
+ mFromTo.put(from, to);
+ }
+
+ @GuardedBy("mLock")
+ private void extractAliasesLocked(List<ResolveInfo> components) {
for (ResolveInfo ri : components) {
final ComponentInfo ci = ri.getComponentInfo();
final ComponentName from = ci.getComponentName();
@@ -186,10 +285,7 @@
if (to == null) {
continue;
}
- if (DEBUG) {
- Slog.d(TAG, "" + from.flattenToShortString() + " -> " + to.flattenToShortString());
- }
- mFromTo.put(from, to);
+ validateAndAddAliasLocked(from, to);
}
}
@@ -221,16 +317,12 @@
continue;
}
- if (DEBUG) {
- Slog.d(TAG,
- "" + from.flattenToShortString() + " -> " + to.flattenToShortString());
- }
- mFromTo.put(from, to);
+ validateAndAddAliasLocked(from, to);
}
}
}
- private ComponentName unflatten(String name) {
+ private static ComponentName unflatten(String name) {
final ComponentName cn = ComponentName.unflattenFromString(name);
if (cn != null) {
return cn;
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 534bd84..c49e696 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -271,9 +271,11 @@
}
}
- void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
+ boolean registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
if (!(sender instanceof PendingIntentRecord)) {
- return;
+ Slog.w(TAG, "registerIntentSenderCancelListener called on non-PendingIntentRecord");
+ // In this case, it's not "success", but we don't know if it's canceld either.
+ return true;
}
boolean isCancelled;
synchronized (mLock) {
@@ -281,12 +283,9 @@
isCancelled = pendingIntent.canceled;
if (!isCancelled) {
pendingIntent.registerCancelListenerLocked(receiver);
- }
- }
- if (isCancelled) {
- try {
- receiver.send(Activity.RESULT_CANCELED, null);
- } catch (RemoteException e) {
+ return true;
+ } else {
+ return false;
}
}
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 97ed0a38..3e59eea 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -947,9 +947,9 @@
}
public void postNotification() {
- final int appUid = appInfo.uid;
- final int appPid = app.getPid();
if (isForeground && foregroundNoti != null) {
+ final int appUid = appInfo.uid;
+ final int appPid = app.getPid();
// Do asynchronous communication with notification manager to
// avoid deadlocks.
final String localPackageName = packageName;
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index db2ecc5..19dcee4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -696,7 +696,7 @@
idpw.print("User Level Hibernation States, ");
idpw.printPair("user", userId);
idpw.println();
- Map<String, UserLevelState> stateMap = mUserStates.get(i);
+ Map<String, UserLevelState> stateMap = mUserStates.get(userId);
idpw.increaseIndent();
for (UserLevelState state : stateMap.values()) {
idpw.print(state);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index cfd2978..fe6a5a3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4562,8 +4562,9 @@
}
if (pkgUid != Process.INVALID_UID) {
if (pkgUid != UserHandle.getAppId(uid)) {
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package " + packageName + " under uid "
+ //TODO 195339480: replace true with debug
+ String otherUidMessage = true ? " but it is really " + pkgUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName + "\" under uid "
+ UserHandle.getAppId(uid) + otherUidMessage);
}
return new PackageVerificationResult(RestrictionBypass.UNRESTRICTED,
@@ -4618,8 +4619,9 @@
}
if (pkgUid != uid) {
- String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not";
- throw new SecurityException("Specified package " + packageName + " under uid " + uid
+ //TODO 195339480: replace true with debug
+ String otherUidMessage = true ? " but it is really " + pkgUid : " but it is not";
+ throw new SecurityException("Specified package \"" + packageName + "\" under uid " + uid
+ otherUidMessage);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d12cf70..8b083bb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -201,7 +201,8 @@
*/
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
- AccessibilityManager.AccessibilityServicesStateChangeListener {
+ AccessibilityManager.AccessibilityServicesStateChangeListener,
+ AudioSystemAdapter.OnRoutingUpdatedListener {
private static final String TAG = "AS.AudioService";
@@ -315,12 +316,14 @@
private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
+ private static final int MSG_ROUTING_UPDATED = 41;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
+ private static final int MSG_INIT_SPATIALIZER = 102;
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -871,6 +874,8 @@
mSfxHelper = new SoundEffectsHelper(mContext);
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
+
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -1035,6 +1040,9 @@
// done with service initialization, continue additional work in our Handler thread
queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+ queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
+ 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
}
/**
@@ -1224,6 +1232,22 @@
updateVibratorInfos();
}
+ //-----------------------------------------------------------------
+ // routing monitoring from AudioSystemAdapter
+ @Override
+ public void onRoutingUpdatedFromNative() {
+ sendMsg(mAudioHandler,
+ MSG_ROUTING_UPDATED,
+ SENDMSG_REPLACE, 0, 0, null,
+ /*delay*/ 0);
+ }
+
+ void monitorRoutingChanges(boolean enabled) {
+ mAudioSystem.setRoutingListener(enabled ? this : null);
+ }
+
+
+ //-----------------------------------------------------------------
RoleObserver mRoleObserver;
class RoleObserver implements OnRoleHoldersChangedListener {
@@ -1408,6 +1432,9 @@
}
}
+ // TODO check property if feature enabled
+ mSpatializerHelper.reset(/* featureEnabled */ SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
@@ -7549,6 +7576,13 @@
mAudioEventWakeLock.release();
break;
+ case MSG_INIT_SPATIALIZER:
+ mSpatializerHelper.init();
+ // TODO read property to see if enabled
+ mSpatializerHelper.setFeatureEnabled(SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -7681,6 +7715,10 @@
case MSG_DISPATCH_AUDIO_MODE:
dispatchMode(msg.arg1);
break;
+
+ case MSG_ROUTING_UPDATED:
+ mSpatializerHelper.onRoutingUpdated();
+ break;
}
}
}
@@ -8249,7 +8287,8 @@
}
//==========================================================================================
- private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
+ private final @NonNull SpatializerHelper mSpatializerHelper;
+ private static final boolean SPATIALIZER_FEATURE_ENABLED_DEFAULT = false;
private void enforceModifyDefaultAudioEffectsPermission() {
if (mContext.checkCallingOrSelfPermission(
@@ -8259,9 +8298,12 @@
}
}
- /** @see AudioManager#getSpatializerImmersiveAudioLevel() */
+ /**
+ * Returns the immersive audio level that the platform is capable of
+ * @see Spatializer#getImmersiveAudioLevel()
+ */
public int getSpatializerImmersiveAudioLevel() {
- return mSpatializerHelper.getImmersiveAudioLevel();
+ return mSpatializerHelper.getCapableImmersiveAudioLevel();
}
/** @see Spatializer#isEnabled() */
@@ -8277,7 +8319,7 @@
/** @see Spatializer#setSpatializerEnabled(boolean) */
public void setSpatializerEnabled(boolean enabled) {
enforceModifyDefaultAudioEffectsPermission();
- mSpatializerHelper.setEnabled(enabled);
+ mSpatializerHelper.setFeatureEnabled(enabled);
}
/** @see Spatializer#canBeSpatialized() */
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 6d56780..ac212ee 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,6 +17,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
@@ -24,6 +25,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -59,6 +62,9 @@
private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
+ private static final Object sRoutingListenerLock = new Object();
+ @GuardedBy("sRoutingListenerLock")
+ private static @Nullable OnRoutingUpdatedListener sRoutingListener;
/**
* should be false except when trying to debug caching errors. When true, the value retrieved
@@ -76,6 +82,23 @@
Log.d(TAG, "---- onRoutingUpdated (from native) ----------");
}
invalidateRoutingCache();
+ final OnRoutingUpdatedListener listener;
+ synchronized (sRoutingListenerLock) {
+ listener = sRoutingListener;
+ }
+ if (listener != null) {
+ listener.onRoutingUpdatedFromNative();
+ }
+ }
+
+ interface OnRoutingUpdatedListener {
+ void onRoutingUpdatedFromNative();
+ }
+
+ static void setRoutingListener(@Nullable OnRoutingUpdatedListener listener) {
+ synchronized (sRoutingListenerLock) {
+ sRoutingListener = listener;
+ }
}
/**
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 708d9e1..32ac785 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -17,9 +17,13 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
+import android.media.AudioSystem;
+import android.media.INativeSpatializerCallback;
+import android.media.ISpatializer;
import android.media.ISpatializerCallback;
import android.media.Spatializer;
import android.os.RemoteCallbackList;
@@ -28,6 +32,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* A helper class to manage Spatializer related functionality
@@ -35,12 +40,175 @@
public class SpatializerHelper {
private static final String TAG = "AS.SpatializerHelper";
+ private static final boolean DEBUG = true;
+
+ private static void logd(String s) {
+ if (DEBUG) {
+ Log.i(TAG, s);
+ }
+ }
+
+ private final @NonNull AudioSystemAdapter mASA;
+ private final @NonNull AudioService mAudioService;
+
+ //------------------------------------------------------------
+ // Spatializer state machine
+ private static final int STATE_UNINITIALIZED = 0;
+ private static final int STATE_NOT_SUPPORTED = 1;
+ private static final int STATE_DISABLED_UNAVAILABLE = 3;
+ private static final int STATE_ENABLED_UNAVAILABLE = 4;
+ private static final int STATE_ENABLED_AVAILABLE = 5;
+ private static final int STATE_DISABLED_AVAILABLE = 6;
+ private int mState = STATE_UNINITIALIZED;
+
+ /** current level as reported by native Spatializer in callback */
+ private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private @Nullable ISpatializer mSpat;
+ private @Nullable SpatializerCallback mSpatCallback;
+
+ // default attributes and format that determine basic availability of spatialization
+ private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ private static final AudioFormat DEFAULT_FORMAT = new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(48000)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
+ .build();
+ // device array to store the routing for the default attributes and format, size 1 because
+ // media is never expected to be duplicated
+ private static final AudioDeviceAttributes[] ROUTING_DEVICES = new AudioDeviceAttributes[1];
//---------------------------------------------------------------
// audio device compatibility / enabled
private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+ //------------------------------------------------------
+ // initialization
+ SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa) {
+ mAudioService = mother;
+ mASA = asa;
+ }
+
+ synchronized void init() {
+ Log.i(TAG, "Initializing");
+ if (mState != STATE_UNINITIALIZED) {
+ throw new IllegalStateException(("init() called in state:" + mState));
+ }
+ // is there a spatializer?
+ mSpatCallback = new SpatializerCallback();
+ final ISpatializer spat = AudioSystem.getSpatializer(mSpatCallback);
+ if (spat == null) {
+ Log.i(TAG, "init(): No Spatializer found");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ // capabilities of spatializer?
+ try {
+ byte[] levels = spat.getSupportedLevels();
+ if (levels == null
+ || levels.length == 0
+ || (levels.length == 1
+ && levels[0] == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE)) {
+ Log.e(TAG, "Spatializer is useless");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ for (byte level : levels) {
+ logd("found support for level: " + level);
+ if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL) {
+ logd("Setting Spatializer to LEVEL_MULTICHANNEL");
+ mCapableSpatLevel = level;
+ break;
+ }
+ }
+ } catch (RemoteException e) {
+ /* capable level remains at NONE*/
+ } finally {
+ if (spat != null) {
+ try {
+ spat.release();
+ } catch (RemoteException e) { /* capable level remains at NONE*/ }
+ }
+ }
+ if (mCapableSpatLevel == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ mState = STATE_DISABLED_UNAVAILABLE;
+ // note at this point mSpat is still not instantiated
+ }
+
+ /**
+ * Like init() but resets the state and spatializer levels
+ * @param featureEnabled
+ */
+ synchronized void reset(boolean featureEnabled) {
+ Log.i(TAG, "Resetting");
+ mState = STATE_UNINITIALIZED;
+ mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ init();
+ setFeatureEnabled(featureEnabled);
+ }
+
+ //------------------------------------------------------
+ // routing monitoring
+ void onRoutingUpdated() {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ break;
+ }
+ mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ final boolean able =
+ AudioSystem.canBeSpatialized(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+ logd("onRoutingUpdated: can spatialize media 5.1:" + able
+ + " on device:" + ROUTING_DEVICES[0]);
+ setDispatchAvailableState(able);
+ }
+
+ //------------------------------------------------------
+ // spatializer callback from native
+ private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
+
+ public void onLevelChanged(byte level) {
+ logd("SpatializerCallback.onLevelChanged level:" + level);
+ synchronized (SpatializerHelper.this) {
+ mSpatLevel = level;
+ }
+ // TODO use reported spat level to change state
+ }
+
+ public void onHeadTrackingModeChanged(byte mode) {
+ logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + mode);
+ }
+
+ public void onHeadToSoundStagePoseUpdated(float[] headToStage) {
+ if (headToStage == null) {
+ Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
+ return;
+ }
+ if (DEBUG) {
+ // 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
+ StringBuilder t = new StringBuilder(42);
+ for (float val : headToStage) {
+ t.append("[").append(String.format(Locale.ENGLISH, "%.3f", val)).append("]");
+ }
+ logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
+ }
+ }
+ };
+
+ //------------------------------------------------------
+ // compatible devices
/**
* @return a shallow copy of the list of compatible audio devices
*/
@@ -59,37 +227,72 @@
}
//------------------------------------------------------
- // enabled state
-
- // global state of feature
- boolean mFeatureEnabled = false;
- // initialized state, checked after each audio_server start
- boolean mInitialized = false;
+ // states
synchronized boolean isEnabled() {
- return mFeatureEnabled;
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ return false;
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
+ }
}
synchronized boolean isAvailable() {
- if (!mInitialized) {
- return false;
- }
- // TODO check device compatibility
- // ...
- return true;
- }
-
- synchronized void setEnabled(boolean enabled) {
- final boolean oldState = mFeatureEnabled;
- mFeatureEnabled = enabled;
- if (oldState != enabled) {
- dispatchEnabledState();
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
}
}
- public int getImmersiveAudioLevel() {
- // TODO replace placeholder code with actual effect discovery
- return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ synchronized void setFeatureEnabled(boolean enabled) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ if (enabled) {
+ throw(new IllegalStateException("Can't enable when uninitialized"));
+ }
+ return;
+ case STATE_NOT_SUPPORTED:
+ if (enabled) {
+ Log.e(TAG, "Can't enable when unsupported");
+ }
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ if (enabled) {
+ createSpat();
+ break;
+ } else {
+ // already in disabled state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (!enabled) {
+ releaseSpat();
+ break;
+ } else {
+ // already in enabled state
+ return;
+ }
+ }
+ setDispatchFeatureEnabledState(enabled);
+ }
+
+ synchronized int getCapableImmersiveAudioLevel() {
+ return mCapableSpatLevel;
}
final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
@@ -105,12 +308,94 @@
mStateCallbacks.unregister(callback);
}
- private synchronized void dispatchEnabledState() {
+ /**
+ * precondition: mState = STATE_*
+ * isFeatureEnabled() != featureEnabled
+ * @param featureEnabled
+ */
+ private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled) {
+ if (featureEnabled) {
+ switch (mState) {
+ case STATE_DISABLED_UNAVAILABLE:
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ case STATE_DISABLED_AVAILABLE:
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ default:
+ throw(new IllegalStateException("Invalid mState:" + mState
+ + " for enabled true"));
+ }
+ } else {
+ switch (mState) {
+ case STATE_ENABLED_UNAVAILABLE:
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ case STATE_ENABLED_AVAILABLE:
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ default:
+ throw (new IllegalStateException("Invalid mState:" + mState
+ + " for enabled false"));
+ }
+ }
final int nbCallbacks = mStateCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mStateCallbacks.getBroadcastItem(i)
- .dispatchSpatializerEnabledChanged(mFeatureEnabled);
+ .dispatchSpatializerEnabledChanged(featureEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
+ }
+ }
+ mStateCallbacks.finishBroadcast();
+ // TODO persist enabled state
+ }
+
+ private synchronized void setDispatchAvailableState(boolean available) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ throw(new IllegalStateException(
+ "Should not update available state in state:" + mState));
+ case STATE_DISABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_DISABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ }
+ case STATE_ENABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ }
+ }
+ final int nbCallbacks = mStateCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mStateCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerAvailableChanged(available);
} catch (RemoteException e) {
Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
}
@@ -119,10 +404,72 @@
}
//------------------------------------------------------
+ // native Spatializer management
+
+ /**
+ * precondition: mState == STATE_DISABLED_*
+ */
+ private void createSpat() {
+ if (mSpat == null) {
+ mSpatCallback = new SpatializerCallback();
+ mSpat = AudioSystem.getSpatializer(mSpatCallback);
+ try {
+ mSpat.setLevel((byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set spatializer level", e);
+ mState = STATE_NOT_SUPPORTED;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+ }
+ }
+
+ /**
+ * precondition: mState == STATE_ENABLED_*
+ */
+ private void releaseSpat() {
+ if (mSpat != null) {
+ mSpatCallback = null;
+ try {
+ mSpat.release();
+ mSpat = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set release spatializer cleanly", e);
+ }
+ }
+ }
+
+ //------------------------------------------------------
// virtualization capabilities
synchronized boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
- // TODO hook up to spatializer effect for query
- return false;
+ logd("canBeSpatialized usage:" + attributes.getUsage()
+ + " format:" + format.toLogFriendlyString());
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ logd("canBeSpatialized false due to state:" + mState);
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ break;
+ }
+
+ // filter on AudioAttributes usage
+ switch (attributes.getUsage()) {
+ case AudioAttributes.USAGE_MEDIA:
+ case AudioAttributes.USAGE_GAME:
+ break;
+ default:
+ logd("canBeSpatialized false due to usage:" + attributes.getUsage());
+ return false;
+ }
+ AudioDeviceAttributes[] devices =
+ // going through adapter to take advantage of routing cache
+ (AudioDeviceAttributes[]) mASA.getDevicesForAttributes(attributes).toArray();
+ final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
+ logd("canBeSpatialized returning " + able);
+ return able;
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index b42f898..9c8ccd9 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -47,6 +47,7 @@
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -766,8 +767,9 @@
if (isUdfps && udfpsProps.length == 3) {
return new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
- componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken, udfpsProps[0],
- udfpsProps[1], udfpsProps[2]);
+ componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken,
+ List.of(new SensorLocationInternal("" /* display */,
+ udfpsProps[0], udfpsProps[1], udfpsProps[2])));
} else {
return new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 996f0fd..4f7c6b0 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -50,7 +50,6 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
-import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -62,7 +61,6 @@
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import java.util.List;
@@ -541,14 +539,4 @@
throw new IllegalArgumentException("Unknown strength: " + strength);
}
}
-
- public static int getUdfpsAuthReason(@NonNull AuthenticationClient<?> client) {
- if (client.isKeyguard()) {
- return IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD;
- } else if (client.isBiometricPrompt()) {
- return IUdfpsOverlayController.REASON_AUTH_BP;
- } else {
- return IUdfpsOverlayController.REASON_AUTH_FPM_OTHER;
- }
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 6f38ed0..85d6d7f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -457,4 +458,14 @@
public boolean wasAuthAttempted() {
return mAuthAttempted;
}
+
+ protected int getShowOverlayReason() {
+ if (isKeyguard()) {
+ return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+ } else if (isBiometricPrompt()) {
+ return BiometricOverlayConstants.REASON_AUTH_BP;
+ } else {
+ return BiometricOverlayConstants.REASON_AUTH_OTHER;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 9191b8b..2826e0c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -128,4 +130,15 @@
public boolean interruptsPrecedingClients() {
return true;
}
+
+ protected int getOverlayReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+ switch (reason) {
+ case FingerprintManager.ENROLL_FIND_SENSOR:
+ return BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+ case FingerprintManager.ENROLL_ENROLL:
+ return BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
+ default:
+ return BiometricOverlayConstants.REASON_UNKNOWN;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
new file mode 100644
index 0000000..6e2b9ef
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
@@ -0,0 +1,144 @@
+/*
+ * 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.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Single entry point & holder for controllers managing UI overlays for biometrics.
+ *
+ * For common operations, like {@link #show(int, int, AcquisitionClient)}, modalities are
+ * skipped if they are not present (provided as null via the constructor).
+ *
+ * Use the getters, such as {@link #ifUdfps(OverlayControllerConsumer)}, to get a controller for
+ * operations that are unique to a single modality.
+ */
+public final class SensorOverlays {
+
+ private static final String TAG = "SensorOverlays";
+
+ @NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController;
+ @NonNull private final Optional<ISidefpsController> mSidefpsController;
+
+ /**
+ * Create an overlay controller for each modality.
+ *
+ * @param udfpsOverlayController under display fps or null if not present on device
+ * @param sidefpsController side fps or null if not present on device
+ */
+ public SensorOverlays(
+ @Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController) {
+ mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController);
+ mSidefpsController = Optional.ofNullable(sidefpsController);
+ }
+
+ /**
+ * Show the overlay.
+ *
+ * @param sensorId sensor id
+ * @param reason reason for showing
+ * @param client client performing operation
+ */
+ public void show(int sensorId, @BiometricOverlayConstants.ShowReason int reason,
+ @NonNull AcquisitionClient<?> client) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().show();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+ }
+ }
+
+ if (mUdfpsOverlayController.isPresent()) {
+ final IUdfpsOverlayControllerCallback callback =
+ new IUdfpsOverlayControllerCallback.Stub() {
+ @Override
+ public void onUserCanceled() {
+ client.onUserCanceled();
+ }
+ };
+
+ try {
+ mUdfpsOverlayController.get().showUdfpsOverlay(sensorId, reason, callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
+ }
+ }
+ }
+
+ /**
+ * Hide the overlay.
+ *
+ * @param sensorId sensor id
+ */
+ public void hide(int sensorId) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().hide();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+ }
+ }
+
+ if (mUdfpsOverlayController.isPresent()) {
+ try {
+ mUdfpsOverlayController.get().hideUdfpsOverlay(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
+ }
+ }
+ }
+
+ /**
+ * Use the udfps controller, if present.
+ * @param consumer action
+ */
+ public void ifUdfps(OverlayControllerConsumer<IUdfpsOverlayController> consumer) {
+ if (mUdfpsOverlayController.isPresent()) {
+ try {
+ consumer.accept(mUdfpsOverlayController.get());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception using overlay controller", e);
+ }
+ }
+ }
+
+ /**
+ * Consumer for a biometric overlay controller.
+ *
+ * This behaves like a normal {@link Consumer} except that it will trap and log
+ * any thrown {@link RemoteException}.
+ *
+ * @param <T> the type of the input to the operation
+ **/
+ @FunctionalInterface
+ public interface OverlayControllerConsumer<T> {
+ /** Perform the operation. */
+ void accept(T t) throws RemoteException;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java
deleted file mode 100644
index 474066c..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors.fingerprint;
-
-import android.annotation.Nullable;
-import android.hardware.fingerprint.ISidefpsController;
-import android.os.RemoteException;
-import android.util.Slog;
-
-/**
- * Contains helper methods for side-fps fingerprint controller.
- */
-public class SidefpsHelper {
- private static final String TAG = "SidefpsHelper";
-
- /**
- * Shows the side-fps affordance
- * @param sidefpsController controller that shows and hides the side-fps affordance
- */
- public static void showOverlay(@Nullable ISidefpsController sidefpsController) {
- if (sidefpsController == null) {
- return;
- }
-
- try {
- sidefpsController.show();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
- }
- }
-
- /**
- * Hides the side-fps affordance
- * @param sidefpsController controller that shows and hides the side-fps affordance
- */
- public static void hideOverlay(@Nullable ISidefpsController sidefpsController) {
- if (sidefpsController == null) {
- return;
- }
- try {
- sidefpsController.hide();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index 879c8a0..29661d4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -17,17 +17,12 @@
package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-
/**
* Contains helper methods for under-display fingerprint HIDL.
*/
@@ -68,88 +63,6 @@
}
}
- public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
- switch (reason) {
- case FingerprintManager.ENROLL_FIND_SENSOR:
- return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR;
- case FingerprintManager.ENROLL_ENROLL:
- return IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
- default:
- return IUdfpsOverlayController.REASON_UNKNOWN;
- }
- }
-
- public static void showUdfpsOverlay(int sensorId, int reason,
- @Nullable IUdfpsOverlayController udfpsOverlayController,
- @NonNull AcquisitionClient<?> client) {
- if (udfpsOverlayController == null) {
- return;
- }
-
- final IUdfpsOverlayControllerCallback callback =
- new IUdfpsOverlayControllerCallback.Stub() {
- @Override
- public void onUserCanceled() {
- client.onUserCanceled();
- }
- };
-
- try {
- udfpsOverlayController.showUdfpsOverlay(sensorId, reason, callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
- }
- }
-
- public static void hideUdfpsOverlay(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.hideUdfpsOverlay(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
- }
- }
-
- public static void onAcquiredGood(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
-
- try {
- udfpsOverlayController.onAcquiredGood(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onAcquiredGood", e);
- }
- }
-
- public static void onEnrollmentProgress(int sensorId, int remaining,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.onEnrollmentProgress(sensorId, remaining);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onEnrollmentProgress", e);
- }
- }
-
- public static void onEnrollmentHelp(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.onEnrollmentHelp(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onEnrollmentHelp", e);
- }
- }
-
public static boolean isValidAcquisitionMessage(@NonNull Context context,
int acquireInfo, int vendorCode) {
return FingerprintManager.getAcquiredString(context, acquireInfo, vendorCode) != null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 9d911e0..8a11e8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -27,20 +27,20 @@
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import java.util.ArrayList;
@@ -53,7 +53,7 @@
private static final String TAG = "FingerprintAuthenticationClient";
@NonNull private final LockoutCache mLockoutCache;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
@@ -68,6 +68,7 @@
int sensorId, boolean isStrongBiometric, int statsClient,
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
@@ -77,7 +78,7 @@
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutCache = lockoutCache;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
}
@@ -120,7 +121,7 @@
if (authenticated) {
mState = STATE_STOPPED;
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
}
@@ -131,7 +132,7 @@
// For UDFPS, notify SysUI that the illumination can be turned off.
// See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
- UdfpsHelper.onAcquiredGood(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
}
super.onAcquired(acquiredInfo, vendorCode);
@@ -145,27 +146,28 @@
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+
try {
mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
mCancellationSignal.cancel();
} catch (RemoteException e) {
@@ -235,7 +237,7 @@
Slog.e(TAG, "Remote exception", e);
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
@@ -252,7 +254,7 @@
Slog.e(TAG, "Remote exception", e);
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index da91cdd..ac3ce89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
@@ -31,7 +32,7 @@
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.DetectionConsumer;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+import com.android.server.biometrics.sensors.SensorOverlays;
/**
* Performs fingerprint detection without exposing any matching information (e.g. accept/reject
@@ -42,8 +43,7 @@
private static final String TAG = "FingerprintDetectClient";
private final boolean mIsStrongBiometric;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
-
+ @NonNull private final SensorOverlays mSensorOverlays;
@Nullable private ICancellationSignal mCancellationSignal;
FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@@ -57,7 +57,7 @@
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
}
@Override
@@ -68,7 +68,8 @@
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
mCancellationSignal.cancel();
} catch (RemoteException e) {
@@ -79,14 +80,13 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+
try {
mCancellationSignal = getFreshDaemon().detectInteraction();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting finger detect", e);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index c420c5c..ccb34aa 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -39,8 +39,8 @@
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
-import com.android.server.biometrics.sensors.fingerprint.SidefpsHelper;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -49,8 +49,7 @@
private static final String TAG = "FingerprintEnrollClient";
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
- @Nullable private final ISidefpsController mSidefpsController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
@@ -63,7 +62,7 @@
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @Nullable IUdfpsOverlayController udfpsOvelayController,
+ @Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
@@ -71,8 +70,7 @@
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
!sensorProps.isAnyUdfpsType() /* shouldVibrate */);
mSensorProps = sensorProps;
- mUdfpsOverlayController = udfpsOvelayController;
- mSidefpsController = sidefpsController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
mEnrollReason = enrollReason;
@@ -91,11 +89,11 @@
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
- UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(
+ controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
}
@@ -106,12 +104,14 @@
if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
&& mSensorProps.isAnyUdfpsType()) {
vibrateSuccess();
- UdfpsHelper.onAcquiredGood(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
}
- if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
- UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
- }
+ mSensorOverlays.ifUdfps(controller -> {
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ controller.onEnrollmentHelp(getSensorId());
+ }
+ });
super.onAcquired(acquiredInfo, vendorCode);
}
@@ -120,8 +120,7 @@
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
@@ -133,8 +132,8 @@
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
+
if (mCancellationSignal != null) {
try {
mCancellationSignal.cancel();
@@ -149,10 +148,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
- mUdfpsOverlayController, this);
- SidefpsHelper.showOverlay(mSidefpsController);
+ mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
mCancellationSignal = getFreshDaemon().enroll(
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index ca83dda..0defc3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -25,10 +25,12 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -145,6 +147,8 @@
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
+
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -164,9 +168,12 @@
componentInfo,
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */,
- prop.sensorLocations[0].sensorLocationX,
- prop.sensorLocations[0].sensorLocationY,
- prop.sensorLocations[0].sensorRadius);
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ List.of(new SensorLocationInternal(
+ "" /* displayId */,
+ prop.sensorLocations[0].sensorLocationX,
+ prop.sensorLocations[0].sensorLocationY,
+ prop.sensorLocations[0].sensorRadius)));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
@@ -403,7 +410,7 @@
userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
- mUdfpsOverlayController, allowBackgroundAuthentication,
+ mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -647,4 +654,45 @@
void setTestHalEnabled(boolean enabled) {
mTestHalEnabled = enabled;
}
+
+ // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL)
+ // reads values via an overlay instead of querying the HAL
+ @NonNull
+ private List<SensorLocationInternal> getWorkaroundSensorProps(@NonNull Context context) {
+ final List<SensorLocationInternal> sensorLocations = new ArrayList<>();
+
+ final TypedArray sfpsProps = context.getResources().obtainTypedArray(
+ com.android.internal.R.array.config_sfps_sensor_props);
+ for (int i = 0; i < sfpsProps.length(); i++) {
+ final int id = sfpsProps.getResourceId(i, -1);
+ if (id > 0) {
+ final SensorLocationInternal location = parseSensorLocation(
+ context.getResources().obtainTypedArray(id));
+ if (location != null) {
+ sensorLocations.add(location);
+ }
+ }
+ }
+ sfpsProps.recycle();
+
+ return sensorLocations;
+ }
+
+ @Nullable
+ private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
+ if (array == null) {
+ return null;
+ }
+
+ try {
+ return new SensorLocationInternal(
+ array.getString(0),
+ array.getInt(1, 0),
+ array.getInt(2, 0),
+ array.getInt(3, 0));
+ } catch (Exception e) {
+ Slog.w(getTag(), "malformed sensor location", e);
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index d2882aa4..5f2f4cf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -629,7 +629,8 @@
mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
restricted, opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId, isStrongBiometric, statsClient,
- mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
+ mTaskStackListener, mLockoutTracker,
+ mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 79ad8e1..dd68b4d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -426,8 +426,7 @@
mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId,
sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- resetLockoutRequiresHardwareAuthToken, sensorProps.sensorLocationX,
- sensorProps.sensorLocationY, sensorProps.sensorRadius);
+ resetLockoutRequiresHardwareAuthToken, sensorProps.getAllLocations());
mMockHalResultController = controller;
mUserHasTrust = new SparseBooleanArray();
mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 7d95ec0..3058e25 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,16 +26,17 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -52,7 +53,7 @@
private static final String TAG = "Biometrics/FingerprintAuthClient";
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
@@ -67,6 +68,7 @@
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
@@ -76,7 +78,7 @@
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
}
@@ -112,7 +114,7 @@
if (authenticated) {
mState = STATE_STOPPED;
resetFailedAttempts(getTargetUserId());
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
final @LockoutTracker.LockoutMode int lockoutMode =
@@ -125,7 +127,7 @@
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -140,7 +142,7 @@
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
}
private void resetFailedAttempts(int userId) {
@@ -168,8 +170,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().authenticate(mOperationId, getTargetUserId());
@@ -177,14 +179,15 @@
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 147a206..b854fb3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -33,6 +34,7 @@
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -48,7 +50,7 @@
private static final String TAG = "FingerprintDetectClient";
private final boolean mIsStrongBiometric;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private boolean mIsPointerDown;
public FingerprintDetectClient(@NonNull Context context,
@@ -61,13 +63,14 @@
true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
setRequestId(requestId);
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
mIsStrongBiometric = isStrongBiometric;
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -86,16 +89,15 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+
try {
getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index dc70534..1ebf44c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -35,7 +35,7 @@
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
-import com.android.server.biometrics.sensors.fingerprint.SidefpsHelper;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -49,8 +49,7 @@
private static final String TAG = "FingerprintEnrollClient";
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
- @Nullable private final ISidefpsController mSidefpsController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private final @FingerprintManager.EnrollReason int mEnrollReason;
private boolean mIsPointerDown;
@@ -65,8 +64,7 @@
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
- mUdfpsOverlayController = udfpsOverlayController;
- mSidefpsController = sidefpsController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -95,10 +93,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
- mUdfpsOverlayController, this);
- SidefpsHelper.showOverlay(mSidefpsController);
+ mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
// GroupId was never used. In fact, groupId is always the same as userId.
@@ -107,16 +103,15 @@
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -131,11 +126,11 @@
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
- UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(
+ controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
}
@@ -143,17 +138,18 @@
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
- if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
- UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
- }
+ mSensorOverlays.ifUdfps(controller -> {
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ controller.onEnrollmentHelp(getSensorId());
+ }
+ });
}
@Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 0f97b90..b5846b5 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -36,9 +36,9 @@
import com.android.server.compat.overrides.OverrideValue;
import com.android.server.compat.overrides.RawOverrideValue;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Represents the state of a single compatibility change.
@@ -82,8 +82,8 @@
ChangeListener mListener = null;
- private Map<String, Boolean> mEvaluatedOverrides;
- private Map<String, PackageOverride> mRawOverrides;
+ private ConcurrentHashMap<String, Boolean> mEvaluatedOverrides;
+ private ConcurrentHashMap<String, PackageOverride> mRawOverrides;
public CompatChange(long changeId) {
this(changeId, null, -1, -1, false, false, null, false);
@@ -114,11 +114,11 @@
description, overridable);
// Initialize override maps.
- mEvaluatedOverrides = new HashMap<>();
- mRawOverrides = new HashMap<>();
+ mEvaluatedOverrides = new ConcurrentHashMap<>();
+ mRawOverrides = new ConcurrentHashMap<>();
}
- void registerListener(ChangeListener listener) {
+ synchronized void registerListener(ChangeListener listener) {
if (mListener != null) {
throw new IllegalStateException(
"Listener for change " + toString() + " already registered.");
@@ -131,8 +131,6 @@
* Force the enabled state of this change for a given package name. The change will only take
* effect after that packages process is killed and restarted.
*
- * <p>Note, this method is not thread safe so callers must ensure thread safety.
- *
* @param pname Package name to enable the change for.
* @param enabled Whether or not to enable the change.
*/
@@ -155,14 +153,12 @@
* Tentatively set the state of this change for a given package name.
* The override will only take effect after that package is installed, if applicable.
*
- * <p>Note, this method is not thread safe so callers must ensure thread safety.
- *
* @param packageName Package name to tentatively enable the change for.
* @param override The package override to be set
* @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
*/
- void addPackageOverride(String packageName, PackageOverride override,
+ synchronized void addPackageOverride(String packageName, PackageOverride override,
OverrideAllowedState allowedState, @Nullable Long versionCode) {
if (getLoggingOnly()) {
throw new IllegalArgumentException(
@@ -185,16 +181,17 @@
* @return {@code true} if the recheck yielded a result that requires invalidating caches
* (a deferred override was consolidated or a regular override was removed).
*/
- boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
+ synchronized boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
@Nullable Long versionCode) {
+ if (packageName == null) {
+ return false;
+ }
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
-
// If the app is not installed or no longer has raw overrides, evaluate to false
- if (versionCode == null || !hasRawOverride(packageName) || !allowed) {
+ if (versionCode == null || !mRawOverrides.containsKey(packageName) || !allowed) {
removePackageOverrideInternal(packageName);
return false;
}
-
// Evaluate the override based on its version
int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode);
switch (overrideValue) {
@@ -211,10 +208,6 @@
return true;
}
- boolean hasPackageOverride(String pname) {
- return mRawOverrides.containsKey(pname);
- }
-
/**
* Remove any package override for the given package name, restoring the default behaviour.
*
@@ -224,9 +217,11 @@
* @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
*/
- boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
+ synchronized boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
@Nullable Long versionCode) {
- if (mRawOverrides.remove(pname) != null) {
+ if (mRawOverrides.containsKey(pname)) {
+ allowedState.enforce(getId(), pname);
+ mRawOverrides.remove(pname);
recheckOverride(pname, allowedState, versionCode);
return true;
}
@@ -244,8 +239,11 @@
if (app == null) {
return defaultValue();
}
- if (mEvaluatedOverrides.containsKey(app.packageName)) {
- return mEvaluatedOverrides.get(app.packageName);
+ if (app.packageName != null) {
+ final Boolean enabled = mEvaluatedOverrides.get(app.packageName);
+ if (enabled != null) {
+ return enabled;
+ }
}
if (getDisabled()) {
return false;
@@ -269,9 +267,12 @@
* @return {@code true} if the change should be enabled for the package.
*/
boolean willBeEnabled(String packageName) {
- if (hasRawOverride(packageName)) {
- int eval = mRawOverrides.get(packageName).evaluateForAllVersions();
- switch (eval) {
+ if (packageName == null) {
+ return defaultValue();
+ }
+ final PackageOverride override = mRawOverrides.get(packageName);
+ if (override != null) {
+ switch (override.evaluateForAllVersions()) {
case VALUE_ENABLED:
return true;
case VALUE_DISABLED:
@@ -292,30 +293,12 @@
return !getDisabled();
}
- /**
- * Checks whether a change has an override for a package.
- * @param packageName name of the package
- * @return true if there is such override
- */
- private boolean hasOverride(String packageName) {
- return mEvaluatedOverrides.containsKey(packageName);
- }
-
- /**
- * Checks whether a change has a deferred override for a package.
- * @param packageName name of the package
- * @return true if there is such a deferred override
- */
- private boolean hasRawOverride(String packageName) {
- return mRawOverrides.containsKey(packageName);
- }
-
- void clearOverrides() {
+ synchronized void clearOverrides() {
mRawOverrides.clear();
mEvaluatedOverrides.clear();
}
- void loadOverrides(ChangeOverrides changeOverrides) {
+ synchronized void loadOverrides(ChangeOverrides changeOverrides) {
// Load deferred overrides for backwards compatibility
if (changeOverrides.getDeferred() != null) {
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
@@ -348,7 +331,7 @@
}
}
- ChangeOverrides saveOverrides() {
+ synchronized ChangeOverrides saveOverrides() {
if (mRawOverrides.isEmpty()) {
return null;
}
@@ -406,7 +389,7 @@
return sb.append(")").toString();
}
- private void notifyListener(String packageName) {
+ private synchronized void notifyListener(String packageName) {
if (mListener != null) {
mListener.onCompatChange(packageName);
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 3faffe1..2bf1ccd 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -28,7 +28,6 @@
import android.os.Environment;
import android.text.TextUtils;
import android.util.LongArray;
-import android.util.LongSparseArray;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -55,11 +54,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -76,9 +76,7 @@
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
- private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
- @GuardedBy("mReadWriteLock")
- private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
+ private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
private final OverrideValidatorImpl mOverrideValidator;
private final AndroidBuildClassifier mAndroidBuildClassifier;
@@ -113,21 +111,13 @@
/**
* Adds a change.
*
- * <p>This is intended to be used by code that reads change config from the filesystem. This
- * should be done at system startup time.
- *
- * <p>Any change with the same ID will be overwritten.
+ * <p>This is intended to be used by unit tests only.
*
* @param change the change to add
*/
+ @VisibleForTesting
void addChange(CompatChange change) {
- mReadWriteLock.writeLock().lock();
- try {
- mChanges.put(change.getId(), change);
- invalidateCache();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
+ mChanges.put(change.getId(), change);
}
/**
@@ -143,20 +133,14 @@
*/
long[] getDisabledChanges(ApplicationInfo app) {
LongArray disabled = new LongArray();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- if (!c.isEnabled(app, mAndroidBuildClassifier)) {
- disabled.add(c.getId());
- }
+ for (CompatChange c : mChanges.values()) {
+ if (!c.isEnabled(app, mAndroidBuildClassifier)) {
+ disabled.add(c.getId());
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
- // Note: we don't need to explicitly sort the array, as the behaviour of LongSparseArray
- // (mChanges) ensures it's already sorted.
- return disabled.toArray();
+ final long[] sortedChanges = disabled.toArray();
+ Arrays.sort(sortedChanges);
+ return sortedChanges;
}
/**
@@ -166,15 +150,10 @@
* @return the change ID, or {@code -1} if no change with that name exists
*/
long lookupChangeId(String name) {
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- if (TextUtils.equals(mChanges.valueAt(i).getName(), name)) {
- return mChanges.keyAt(i);
- }
+ for (CompatChange c : mChanges.values()) {
+ if (TextUtils.equals(c.getName(), name)) {
+ return c.getId();
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
return -1;
}
@@ -188,17 +167,12 @@
* change ID is not known, as unknown changes are enabled by default.
*/
boolean isChangeEnabled(long changeId, ApplicationInfo app) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- // we know nothing about this change: default behaviour is enabled.
- return true;
- }
- return c.isEnabled(app, mAndroidBuildClassifier);
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ // we know nothing about this change: default behaviour is enabled.
+ return true;
}
+ return c.isEnabled(app, mAndroidBuildClassifier);
}
/**
@@ -210,17 +184,12 @@
* {@code true} if the change ID is not known, as unknown changes are enabled by default.
*/
boolean willChangeBeEnabled(long changeId, String packageName) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- // we know nothing about this change: default behaviour is enabled.
- return true;
- }
- return c.willBeEnabled(packageName);
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ // we know nothing about this change: default behaviour is enabled.
+ return true;
}
+ return c.willBeEnabled(packageName);
}
/**
@@ -239,7 +208,7 @@
* @return {@code true} if the change existed before adding the override
* @throws IllegalStateException if overriding is not allowed
*/
- boolean addOverride(long changeId, String packageName, boolean enabled) {
+ synchronized boolean addOverride(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(enabled).build());
saveOverrides();
@@ -250,12 +219,11 @@
/**
* Overrides the enabled state for a given change and app.
*
- * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param overrides list of overrides to default changes config.
* @param packageName app for which the overrides will be applied.
*/
- void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
+ synchronized void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
for (Long changeId : overrides.overrides.keySet()) {
addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
}
@@ -265,36 +233,24 @@
private boolean addOverrideUnsafe(long changeId, String packageName,
PackageOverride overrides) {
- boolean alreadyKnown = true;
+ final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName);
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- alreadyKnown = false;
- c = new CompatChange(changeId);
- addChange(c);
- }
- c.addPackageOverride(packageName, overrides, allowedState, versionCode);
- invalidateCache();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- return alreadyKnown;
+
+ final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
+ alreadyKnown.set(false);
+ return new CompatChange(changeId);
+ });
+ c.addPackageOverride(packageName, overrides, allowedState, versionCode);
+ invalidateCache();
+ return alreadyKnown.get();
}
/** Checks whether the change is known to the compat config. */
boolean isKnownChangeId(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null;
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ return mChanges.containsKey(changeId);
}
/**
@@ -302,55 +258,35 @@
* target SDK gated).
*/
int maxTargetSdkForChangeIdOptIn(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c != null && c.getEnableSinceTargetSdk() != -1) {
- return c.getEnableSinceTargetSdk() - 1;
- }
- return -1;
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c != null && c.getEnableSinceTargetSdk() != -1) {
+ return c.getEnableSinceTargetSdk() - 1;
}
+ return -1;
}
/**
* Returns whether the change is marked as logging only.
*/
boolean isLoggingOnly(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getLoggingOnly();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getLoggingOnly();
}
/**
* Returns whether the change is marked as disabled.
*/
boolean isDisabled(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getDisabled();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getDisabled();
}
/**
* Returns whether the change is overridable.
*/
boolean isOverridable(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getOverridable();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getOverridable();
}
/**
@@ -363,10 +299,12 @@
* @param packageName the app package name that was overridden
* @return {@code true} if an override existed;
*/
- boolean removeOverride(long changeId, String packageName) {
+ synchronized boolean removeOverride(long changeId, String packageName) {
boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
- saveOverrides();
- invalidateCache();
+ if (overrideExists) {
+ saveOverrides();
+ invalidateCache();
+ }
return overrideExists;
}
@@ -376,14 +314,9 @@
*/
private boolean removeOverrideUnsafe(long changeId, String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c != null) {
- return removeOverrideUnsafe(c, packageName, versionCode);
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c != null) {
+ return removeOverrideUnsafe(c, packageName, versionCode);
}
return false;
}
@@ -397,13 +330,7 @@
long changeId = change.getId();
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
- if (change.hasPackageOverride(packageName)) {
- allowedState.enforce(changeId, packageName);
- change.removePackageOverride(packageName, allowedState, versionCode);
- invalidateCache();
- return true;
- }
- return false;
+ return change.removePackageOverride(packageName, allowedState, versionCode);
}
/**
@@ -414,19 +341,16 @@
*
* @param packageName the package for which the overrides should be purged
*/
- void removePackageOverrides(String packageName) {
+ synchronized void removePackageOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- removeOverrideUnsafe(change, packageName, versionCode);
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+ boolean shouldInvalidateCache = false;
+ for (CompatChange change : mChanges.values()) {
+ shouldInvalidateCache |= removeOverrideUnsafe(change, packageName, versionCode);
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
}
/**
@@ -439,34 +363,31 @@
* @param overridesToRemove list of change IDs for which to restore the default behaviour.
* @param packageName the package for which the overrides should be purged
*/
- void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
+ synchronized void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
String packageName) {
+ boolean shouldInvalidateCache = false;
for (Long changeId : overridesToRemove.changeIds) {
- removeOverrideUnsafe(changeId, packageName);
+ shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName);
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
}
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
int targetSdkVersion) {
LongArray allowed = new LongArray();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(change.getId(),
- packageName);
- if (allowedState.state == OverrideAllowedState.ALLOWED) {
- allowed.add(change.getId());
- }
+ for (CompatChange change : mChanges.values()) {
+ if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
+ continue;
}
- } finally {
- mReadWriteLock.readLock().unlock();
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(change.getId(),
+ packageName);
+ if (allowedState.state == OverrideAllowedState.ALLOWED) {
+ allowed.add(change.getId());
+ }
}
return allowed.toArray();
}
@@ -479,12 +400,15 @@
*/
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
+ boolean shouldInvalidateCache = false;
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName,
+ shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(true).build());
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
return changes.length;
}
@@ -496,30 +420,27 @@
*/
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
+ boolean shouldInvalidateCache = false;
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName,
+ shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(false).build());
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
return changes.length;
}
boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
- boolean alreadyKnown = true;
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- alreadyKnown = false;
- c = new CompatChange(changeId);
- addChange(c);
- }
- c.registerListener(listener);
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- return alreadyKnown;
+ final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
+ final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
+ alreadyKnown.set(false);
+ invalidateCache();
+ return new CompatChange(changeId);
+ });
+ c.registerListener(listener);
+ return alreadyKnown.get();
}
boolean defaultChangeIdValue(long changeId) {
@@ -537,12 +458,7 @@
@VisibleForTesting
void clearChanges() {
- mReadWriteLock.writeLock().lock();
- try {
- mChanges.clear();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
+ mChanges.clear();
}
/**
@@ -551,18 +467,12 @@
* @param pw {@link PrintWriter} instance to which the information will be dumped
*/
void dumpConfig(PrintWriter pw) {
- mReadWriteLock.readLock().lock();
- try {
- if (mChanges.size() == 0) {
- pw.println("No compat overrides.");
- return;
- }
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- pw.println(c.toString());
- }
- } finally {
- mReadWriteLock.readLock().unlock();
+ if (mChanges.size() == 0) {
+ pw.println("No compat overrides.");
+ return;
+ }
+ for (CompatChange c : mChanges.values()) {
+ pw.println(c.toString());
}
}
@@ -574,18 +484,12 @@
CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
Set<Long> enabled = new HashSet<>();
Set<Long> disabled = new HashSet<>();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
- enabled.add(c.getId());
- } else {
- disabled.add(c.getId());
- }
+ for (CompatChange c : mChanges.values()) {
+ if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
+ enabled.add(c.getId());
+ } else {
+ disabled.add(c.getId());
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled));
}
@@ -596,17 +500,12 @@
* @return an array of {@link CompatibilityChangeInfo} with the current changes
*/
CompatibilityChangeInfo[] dumpChanges() {
- mReadWriteLock.readLock().lock();
- try {
- CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- changeInfos[i] = new CompatibilityChangeInfo(change);
- }
- return changeInfos;
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
+ int i = 0;
+ for (CompatChange change : mChanges.values()) {
+ changeInfos[i++] = new CompatibilityChangeInfo(change);
}
+ return changeInfos;
}
void initConfigFromLib(File libraryDir) {
@@ -626,10 +525,12 @@
Config config = com.android.server.compat.config.XmlParser.read(in);
for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
- addChange(new CompatChange(change));
+ mChanges.put(change.getId(), new CompatChange(change));
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+ } finally {
+ invalidateCache();
}
}
@@ -641,15 +542,12 @@
@VisibleForTesting
void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
// Clear overrides from all changes before loading.
- mReadWriteLock.writeLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- mChanges.valueAt(i).clearOverrides();
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+
+ for (CompatChange c : mChanges.values()) {
+ c.clearOverrides();
}
+
loadOverrides(staticOverridesFile);
mOverridesFile = dynamicOverridesFile;
@@ -698,18 +596,12 @@
}
synchronized (mOverridesFile) {
Overrides overrides = new Overrides();
- mReadWriteLock.readLock().lock();
- try {
- List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
- for (int idx = 0; idx < mChanges.size(); ++idx) {
- CompatChange c = mChanges.valueAt(idx);
- ChangeOverrides changeOverrides = c.saveOverrides();
- if (changeOverrides != null) {
- changeOverridesList.add(changeOverrides);
- }
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (CompatChange c : mChanges.values()) {
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
// Create the file if it doesn't already exist
try {
@@ -741,20 +633,11 @@
void recheckOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
boolean shouldInvalidateCache = false;
- mReadWriteLock.readLock().lock();
- try {
- for (int idx = 0; idx < mChanges.size(); ++idx) {
- CompatChange c = mChanges.valueAt(idx);
- if (!c.hasPackageOverride(packageName)) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
- packageName);
- shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
- }
- } finally {
- mReadWriteLock.readLock().unlock();
+ for (CompatChange c : mChanges.values()) {
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
+ packageName);
+ shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
}
if (shouldInvalidateCache) {
invalidateCache();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c964e37..a7b05ca 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2312,6 +2312,9 @@
int displayId = msg.arg1;
final LogicalDisplay display =
mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (display == null) {
+ break;
+ }
uids = display.getPendingFrameRateOverrideUids();
display.clearPendingFrameRateOverrideUids();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2813236..075b74d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -136,8 +136,6 @@
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionInspector;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto;
@@ -512,14 +510,6 @@
IInputContext mCurInputContext;
/**
- * The missing method flags for the input context last provided by the current client.
- *
- * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
- */
- @MissingMethodFlags
- int mCurInputContextMissingMethods;
-
- /**
* The attributes last provided by the current client.
*/
EditorInfo mCurAttribute;
@@ -1044,6 +1034,7 @@
super(handler);
}
+ @GuardedBy("mMethodMap")
public void registerContentObserverLocked(@UserIdInt int userId) {
if (mRegistered && mUserId == userId) {
return;
@@ -1662,6 +1653,7 @@
mMenuController = new InputMethodMenuController(this);
}
+ @GuardedBy("mMethodMap")
private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME
if (mCurMethodId != null && !mMethodMap.get(mCurMethodId).isSystem()) {
@@ -1712,7 +1704,7 @@
// The mSystemReady flag is set during boot phase,
// and user switch would not happen at that time.
- resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
+ resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER);
buildInputMethodListLocked(initialUserSwitch);
if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
// This is the first time of the user switch and
@@ -2235,6 +2227,7 @@
}
}
+ @GuardedBy("mMethodMap")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
@@ -2258,7 +2251,8 @@
}
}
- private int getImeShowFlags() {
+ @GuardedBy("mMethodMap")
+ private int getImeShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
flags |= InputMethod.SHOW_FORCED
@@ -2269,7 +2263,8 @@
return flags;
}
- private int getAppShowFlags() {
+ @GuardedBy("mMethodMap")
+ private int getAppShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
flags |= InputMethodManager.SHOW_FORCED;
@@ -2308,11 +2303,11 @@
final SessionState session = mCurClient.curSession;
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
- MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
+ MSG_START_INPUT, 0 /* unused */, initial ? 0 : 1 /* restarting */,
startInputToken, session, mCurInputContext, mCurAttribute));
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
- showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
+ showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
final InputMethodInfo curInputMethodInfo = mMethodMap.get(mCurId);
@@ -2326,8 +2321,8 @@
@GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
- @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute,
- @StartInputFlags int startInputFlags, @StartInputReason int startInputReason) {
+ @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
+ @StartInputReason int startInputReason) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
return InputBindResult.NO_IME;
@@ -2384,13 +2379,6 @@
if (mCurSeq <= 0) mCurSeq = 1;
mCurClient = cs;
mCurInputContext = inputContext;
- if (cs.selfReportedDisplayId != displayIdToShowIme) {
- // CursorAnchorInfo API does not work as-is for cross-display scenario. Pretend that
- // InputConnection#requestCursorUpdates() is not implemented in the application so that
- // IMEs will always receive false from this API.
- missingMethods |= MissingMethodFlags.REQUEST_CURSOR_UPDATES;
- }
- mCurInputContextMissingMethods = missingMethods;
mCurAttribute = attribute;
// Check if the input method is changing.
@@ -2572,6 +2560,7 @@
channel.dispose();
}
+ @GuardedBy("mMethodMap")
void unbindCurrentMethodLocked() {
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
@@ -2603,12 +2592,14 @@
clearCurMethodLocked();
}
- void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
+ @GuardedBy("mMethodMap")
+ void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
mCurMethodId = null;
unbindCurrentMethodLocked();
unbindCurrentClientLocked(unbindClientReason);
}
+ @GuardedBy("mMethodMap")
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
@@ -2620,12 +2611,14 @@
}
}
+ @GuardedBy("mMethodMap")
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;
cs.sessionRequested = false;
}
+ @GuardedBy("mMethodMap")
private void finishSessionLocked(SessionState sessionState) {
if (sessionState != null) {
if (sessionState.session != null) {
@@ -2644,6 +2637,7 @@
}
}
+ @GuardedBy("mMethodMap")
void clearCurMethodLocked() {
if (mCurMethod != null) {
final int numClients = mClients.size();
@@ -2727,6 +2721,7 @@
}
}
+ @GuardedBy("mMethodMap")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2790,6 +2785,7 @@
return false;
}
+ @GuardedBy("mMethodMap")
private boolean isKeyguardLocked() {
return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
}
@@ -2856,11 +2852,13 @@
}
}
+ @GuardedBy("mMethodMap")
void updateSystemUiLocked() {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
// Caution! This method is called in this class. Handle multi-user carefully
+ @GuardedBy("mMethodMap")
private void updateSystemUiLocked(int vis, int backDisposition) {
if (mCurToken == null) {
return;
@@ -2928,11 +2926,13 @@
}
}
+ @GuardedBy("mMethodMap")
void updateFromSettingsLocked(boolean enabledMayChange) {
updateInputMethodsFromSettingsLocked(enabledMayChange);
mMenuController.updateKeyboardFromSettingsLocked();
}
+ @GuardedBy("mMethodMap")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
@@ -2973,11 +2973,11 @@
setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
- resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
+ resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED);
}
} else {
// There is no longer an input method set, so stop any current one.
- resetCurrentMethodAndClient(UnbindReason.NO_IME);
+ resetCurrentMethodAndClientLocked(UnbindReason.NO_IME);
}
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
@@ -2987,7 +2987,8 @@
}
- /* package */ void setInputMethodLocked(String id, int subtypeId) {
+ @GuardedBy("mMethodMap")
+ void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + id);
@@ -3135,9 +3136,8 @@
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(
- MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver,
- showInputToken));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
+ getImeShowFlagsLocked(), reason, mCurMethod, resultReceiver, showInputToken));
mInputShown = true;
if (mHaveConnection && !mVisibleBound) {
bindCurrentInputMethodServiceLocked(
@@ -3209,6 +3209,7 @@
}
}
+ @GuardedBy("mMethodMap")
boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
@@ -3262,10 +3263,10 @@
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
- @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
+ int unverifiedTargetSdkVersion) {
return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, attribute, inputContext,
- missingMethods, unverifiedTargetSdkVersion);
+ unverifiedTargetSdkVersion);
}
@NonNull
@@ -3273,7 +3274,7 @@
@StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
- @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
+ int unverifiedTargetSdkVersion) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -3309,8 +3310,7 @@
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
- attribute, inputContext, missingMethods, unverifiedTargetSdkVersion,
- userId);
+ attribute, inputContext, unverifiedTargetSdkVersion, userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3330,20 +3330,18 @@
}
}
+ @GuardedBy("mMethodMap")
@NonNull
private InputBindResult startInputOrWindowGainedFocusInternalLocked(
@StartInputReason int startInputReason, IInputMethodClient client,
@NonNull IBinder windowToken, @StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
- IInputContext inputContext, @MissingMethodFlags int missingMethods,
- int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+ IInputContext inputContext, int unverifiedTargetSdkVersion, @UserIdInt int userId) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
+ " client=" + client.asBinder()
+ " inputContext=" + inputContext
- + " missingMethods="
- + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
+ " attribute=" + attribute
+ " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags)
@@ -3424,8 +3422,8 @@
+ InputMethodDebug.startInputReasonToString(startInputReason));
}
if (attribute != null) {
- return startInputUncheckedLocked(cs, inputContext, missingMethods,
- attribute, startInputFlags, startInputReason);
+ return startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ startInputReason);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3465,8 +3463,8 @@
// UI for input.
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
- startInputFlags, startInputReason);
+ res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ startInputReason);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -3504,8 +3502,8 @@
// is more room for the target window + IME.
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods,
- attribute, startInputFlags, startInputReason);
+ res = startInputUncheckedLocked(cs, inputContext, attribute,
+ startInputFlags, startInputReason);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3535,8 +3533,8 @@
if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods,
- attribute, startInputFlags, startInputReason);
+ res = startInputUncheckedLocked(cs, inputContext, attribute,
+ startInputFlags, startInputReason);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3554,8 +3552,8 @@
unverifiedTargetSdkVersion, startInputFlags)) {
if (!sameWindowFocused) {
if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods,
- attribute, startInputFlags, startInputReason);
+ res = startInputUncheckedLocked(cs, inputContext, attribute,
+ startInputFlags, startInputReason);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3583,8 +3581,8 @@
SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR);
}
}
- res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
- startInputFlags, startInputReason);
+ res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
+ startInputReason);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
@@ -3609,6 +3607,7 @@
return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
}
+ @GuardedBy("mMethodMap")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
final int uid = Binder.getCallingUid();
@@ -4100,6 +4099,7 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ @GuardedBy("mMethodMap")
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -4348,7 +4348,6 @@
// ---------------------------------------------------------
case MSG_START_INPUT: {
- final int missingMethods = msg.arg1;
final boolean restarting = msg.arg2 != 0;
args = (SomeArgs) msg.obj;
final IBinder startInputToken = (IBinder) args.arg1;
@@ -4357,8 +4356,8 @@
final EditorInfo editorInfo = (EditorInfo) args.arg4;
try {
setEnabledSessionInHandlerThread(session);
- session.method.startInput(startInputToken, inputContext, missingMethods,
- editorInfo, restarting);
+ session.method.startInput(startInputToken, inputContext, editorInfo,
+ restarting);
} catch (RemoteException e) {
}
args.recycle();
@@ -4497,6 +4496,7 @@
active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state));
}
+ @GuardedBy("mMethodMap")
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -4732,6 +4732,7 @@
* @param enabled {@code true} if {@code id} needs to be enabled.
* @return {@code true} if the IME was previously enabled. {@code false} otherwise.
*/
+ @GuardedBy("mMethodMap")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
.getEnabledInputMethodsAndSubtypeListLocked();
@@ -4767,6 +4768,7 @@
}
}
+ @GuardedBy("mMethodMap")
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
@@ -4793,6 +4795,7 @@
}
}
+ @GuardedBy("mMethodMap")
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
InputMethodInfo imi = mMethodMap.get(newDefaultIme);
int lastSubtypeId = NOT_A_SUBTYPE_ID;
@@ -4825,6 +4828,7 @@
}
}
+ @GuardedBy("mMethodMap")
InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
if (mCurMethodId == null) {
return null;
@@ -5575,6 +5579,7 @@
* @return {@code false} if it fails to enable the IME. {@code false} otherwise.
*/
@BinderThread
+ @GuardedBy("mMethodMap")
private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
@UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
PrintWriter error) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 489b9b3..dcf415f 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -605,9 +605,20 @@
for (String permission : permissions) {
int opCode = mAppOpsManager.permissionToOpCode(permission);
if (opCode != AppOpsManager.OP_NONE) {
- if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
- != AppOpsManager.MODE_ALLOWED) {
+ // The noteOp call may check for required permissions. Use the below logic to ensure
+ // that the system server permission is enforced at the call.
+ long token = Binder.setCallingWorkSourceUid(android.os.Process.myUid());
+ try {
+ if (mAppOpsManager.noteOp(opCode, mUid, mPackage, mAttributionTag, noteMessage)
+ != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ } catch (SecurityException e) {
+ Log.e(TAG, "SecurityException: noteOp for pkg " + mPackage + " opcode "
+ + opCode + ": " + e.getMessage());
return false;
+ } finally {
+ Binder.restoreCallingWorkSource(token);
}
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index a491eb7..a25392a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -137,6 +137,8 @@
// True if WiFi is available for the Context Hub
private boolean mIsWifiAvailable = false;
+ private boolean mIsWifiScanningEnabled = false;
+ private boolean mIsWifiMainEnabled = false;
// Lock object for sendWifiSettingUpdate()
private final Object mSendWifiSettingUpdateLock = new Object();
@@ -1064,10 +1066,20 @@
private void sendWifiSettingUpdate(boolean forceUpdate) {
synchronized (mSendWifiSettingUpdateLock) {
WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
- boolean enabled = wifiManager.isWifiEnabled() || wifiManager.isScanAlwaysAvailable();
- if (forceUpdate || mIsWifiAvailable != enabled) {
- mIsWifiAvailable = enabled;
- mContextHubWrapper.onWifiSettingChanged(enabled);
+ boolean wifiEnabled = wifiManager.isWifiEnabled();
+ boolean wifiScanEnabled = wifiManager.isScanAlwaysAvailable();
+ boolean wifiAvailable = wifiEnabled || wifiScanEnabled;
+ if (forceUpdate || mIsWifiAvailable != wifiAvailable) {
+ mIsWifiAvailable = wifiAvailable;
+ mContextHubWrapper.onWifiSettingChanged(wifiAvailable);
+ }
+ if (forceUpdate || mIsWifiScanningEnabled != wifiScanEnabled) {
+ mIsWifiScanningEnabled = wifiScanEnabled;
+ mContextHubWrapper.onWifiScanningSettingChanged(wifiScanEnabled);
+ }
+ if (forceUpdate || mIsWifiMainEnabled != wifiEnabled) {
+ mIsWifiMainEnabled = wifiEnabled;
+ mContextHubWrapper.onWifiMainSettingChanged(wifiEnabled);
}
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 1de749b..2cdf75d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -165,7 +165,18 @@
aidlNanoAppBinary.flags = nanoAppBinary.getFlags();
aidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion();
aidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion();
- aidlNanoAppBinary.customBinary = nanoAppBinary.getBinaryNoHeader();
+ // This explicit definition is required to avoid erroneous behavior at the binder.
+ aidlNanoAppBinary.customBinary = new byte[0];
+
+ // Log exceptions while processing the binary, but continue to pass down the binary
+ // since the error checking is deferred to the Context Hub.
+ try {
+ aidlNanoAppBinary.customBinary = nanoAppBinary.getBinaryNoHeader();
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, e.getMessage());
+ } catch (NullPointerException e) {
+ Log.w(TAG, "NanoApp binary was null");
+ }
return aidlNanoAppBinary;
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 6c70d9d..2c82f4a 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -198,6 +198,20 @@
public abstract void onWifiSettingChanged(boolean enabled);
/**
+ * Notifies the Contexthub implementation of a user WiFi main setting change.
+ *
+ * @param enabled true if the WiFi main setting has been enabled.
+ */
+ public abstract void onWifiMainSettingChanged(boolean enabled);
+
+ /**
+ * Notifies the Contexthub implementation of a user WiFi scanning setting change.
+ *
+ * @param enabled true if the WiFi scanning setting has been enabled.
+ */
+ public abstract void onWifiScanningSettingChanged(boolean enabled);
+
+ /**
* @return True if this version of the Contexthub HAL supports airplane mode setting
* notifications.
*/
@@ -335,33 +349,43 @@
return new Pair(hubInfoList, new ArrayList<String>(supportedPermissions));
}
- // TODO(b/194285834): Implement settings logic
public boolean supportsLocationSettingNotifications() {
- return false;
+ return true;
}
public boolean supportsWifiSettingNotifications() {
- return false;
+ return true;
}
public boolean supportsAirplaneModeSettingNotifications() {
- return false;
+ return true;
}
public boolean supportsMicrophoneDisableSettingNotifications() {
- return false;
+ return true;
}
public void onLocationSettingChanged(boolean enabled) {
+ onSettingChanged(android.hardware.contexthub.Setting.LOCATION, enabled);
}
public void onWifiSettingChanged(boolean enabled) {
}
public void onAirplaneModeSettingChanged(boolean enabled) {
+ onSettingChanged(android.hardware.contexthub.Setting.AIRPLANE_MODE, enabled);
}
public void onMicrophoneDisableSettingChanged(boolean enabled) {
+ onSettingChanged(android.hardware.contexthub.Setting.MICROPHONE, enabled);
+ }
+
+ public void onWifiMainSettingChanged(boolean enabled) {
+ onSettingChanged(android.hardware.contexthub.Setting.WIFI_MAIN, enabled);
+ }
+
+ public void onWifiScanningSettingChanged(boolean enabled) {
+ onSettingChanged(android.hardware.contexthub.Setting.WIFI_SCANNING, enabled);
}
@ContextHubTransaction.Result
@@ -414,6 +438,14 @@
return success ? ContextHubTransaction.RESULT_SUCCESS
: ContextHubTransaction.RESULT_FAILED_UNKNOWN;
}
+
+ private void onSettingChanged(byte setting, boolean enabled) {
+ try {
+ mHub.onSettingChanged(setting, enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while sending setting update");
+ }
+ }
}
/**
@@ -531,6 +563,9 @@
mCallback = callback;
mHub.registerCallback(contextHubId, mHidlCallback);
}
+
+ public void onWifiMainSettingChanged(boolean enabled) {}
+ public void onWifiScanningSettingChanged(boolean enabled) {}
}
private static class ContextHubWrapperV1_0 extends ContextHubWrapperHidl {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 3818d74..db0f1eb 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -300,6 +300,8 @@
/**
* Provides system state to AppsFilter via {@link CurrentStateCallback} after properly guarding
* the data with the package lock.
+ *
+ * Don't call {@link #runWithState} with {@link #mCacheLock} held.
*/
@VisibleForTesting(visibility = PRIVATE)
public interface StateProvider {
@@ -923,15 +925,16 @@
}
private void updateShouldFilterCacheForPackage(String packageName) {
- synchronized (mCacheLock) {
- if (mShouldFilterCache != null) {
- mStateProvider.runWithState((settings, users) -> {
- updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */,
- settings.get(packageName), settings, users,
- settings.size() /*maxIndex*/);
- });
+ mStateProvider.runWithState((settings, users) -> {
+ synchronized (mCacheLock) {
+ if (mShouldFilterCache == null) {
+ return;
+ }
+ updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */,
+ settings.get(packageName), settings, users,
+ settings.size() /*maxIndex*/);
}
- }
+ });
}
private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache,
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index ed00609..6225753 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -46,6 +46,7 @@
public static final int DUMP_KNOWN_PACKAGES = 1 << 27;
public static final int DUMP_PER_UID_READ_TIMEOUTS = 1 << 28;
public static final int DUMP_SNAPSHOT_STATISTICS = 1 << 29;
+ public static final int DUMP_PROTECTED_BROADCASTS = 1 << 30;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index fd00106..bf241d4 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -21,7 +21,6 @@
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
@@ -29,23 +28,18 @@
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
-import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
-import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
@@ -54,10 +48,7 @@
import android.content.ContentResolver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
@@ -70,15 +61,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.WatchedArrayMap;
@@ -86,13 +74,9 @@
import java.io.File;
import java.io.IOException;
-import java.security.DigestException;
-import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.ExecutorService;
/**
@@ -284,6 +268,7 @@
}
if (!onlyCore) {
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
// Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
@@ -316,7 +301,7 @@
mPm.removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getPath());
- mPm.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ scanPackageHelper.scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
@@ -371,7 +356,7 @@
mPm.mSettings.enableSystemPackageLPw(packageName);
try {
- final AndroidPackage newPkg = mPm.scanPackageTracedLI(
+ final AndroidPackage newPkg = scanPackageHelper.scanPackageTracedLI(
scanFile, reparseFlags, rescanFlags, 0, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
@@ -456,6 +441,7 @@
fileCount++;
}
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
@@ -471,7 +457,7 @@
parseResult.parsedPackage);
}
try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
+ scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null, platformPackage, isUpgrade,
isPreNMR1Upgrade);
} catch (PackageManagerException e) {
@@ -650,8 +636,9 @@
mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
}
mPm.removePackageLI(stubPkg, true /*chatty*/);
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
- return mPm.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -869,8 +856,10 @@
}
}
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
final AndroidPackage pkg =
- mPm.scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+ scanPackageHelper.scanPackageTracedLI(
+ codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -932,396 +921,4 @@
}
}
}
-
- /**
- * Adds a new package to the internal data structures during platform initialization.
- * <p>After adding, the package is known to the system and available for querying.
- * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
- * etc...], additional checks are performed. Basic verification [such as ensuring
- * matching signatures, checking version codes, etc...] occurs if the package is
- * identical to a previously known package. If the package fails a signature check,
- * the version installed on /data will be removed. If the version of the new package
- * is less than or equal than the version on /data, it will be ignored.
- * <p>Regardless of the package location, the results are applied to the internal
- * structures and the package is made available to the rest of the system.
- * <p>NOTE: The return value should be removed. It's the passed in package object.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
- @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
- boolean isPreNMR1Upgrade) throws PackageManagerException {
- final boolean scanSystemPartition =
- (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
- final String renamedPkgName;
- final PackageSetting disabledPkgSetting;
- final boolean isSystemPkgUpdated;
- final boolean pkgAlreadyExists;
- PackageSetting pkgSetting;
-
- synchronized (mPm.mLock) {
- renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = PackageManagerService.getRealPackageName(parsedPackage,
- renamedPkgName);
- if (realPkgName != null) {
- PackageManagerService.ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = mPm.getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
- pkgAlreadyExists = pkgSetting != null;
- final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.name : parsedPackage.getPackageName();
- if (scanSystemPartition && !pkgAlreadyExists
- && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
- // The updated-package data for /system apk remains inconsistently
- // after the package data for /data apk is lost accidentally.
- // To recover it, enable /system apk and install it as non-updated system app.
- Slog.w(TAG, "Inconsistent package setting of updated system app for "
- + disabledPkgName + ". To recover it, enable the system app"
- + "and install it as non-updated system app.");
- mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
- }
- disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
- isSystemPkgUpdated = disabledPkgSetting != null;
-
- if (DEBUG_INSTALL && isSystemPkgUpdated) {
- Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
- }
-
- final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
- if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
- && sharedUserSetting != null) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
-
- if (scanSystemPartition) {
- if (isSystemPkgUpdated) {
- // we're updating the disabled package, so, scan it as the package setting
- boolean isPlatformPackage = platformPackage != null
- && Objects.equals(platformPackage.getPackageName(),
- parsedPackage.getPackageName());
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- null, disabledPkgSetting /* pkgSetting */,
- null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user, null);
- PackageManagerService.applyPolicy(parsedPackage, scanFlags,
- platformPackage, true);
- final ScanResult scanResult =
- mPm.scanPackageOnlyLI(request, mPm.mInjector,
- mPm.mFactoryTest, -1L);
- if (scanResult.mExistingSettingCopied
- && scanResult.mRequest.mPkgSetting != null) {
- scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
- }
- }
- }
- }
-
- final boolean newPkgChangedPaths = pkgAlreadyExists
- && !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater =
- pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
- final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
- if (isSystemPkgBetter) {
- // The version of the application on /system is greater than the version on
- // /data. Switch back to the application on /system.
- // It's safe to assume the application on /system will correctly scan. If not,
- // there won't be a working copy of the application.
- synchronized (mPm.mLock) {
- // just remove the loaded entries from package lists
- mPm.mPackages.remove(pkgSetting.name);
- }
-
- logCriticalInfo(Log.WARN,
- "System package updated;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString()
- + " --> " + parsedPackage.getPath());
-
- final InstallArgs args = mPm.createInstallArgsForExisting(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
- args.cleanUpResourcesLI();
- synchronized (mPm.mLock) {
- mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
- }
- }
-
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
- if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // In the case of a skipped package, commitReconciledScanResultLocked is not called to
- // add the object to the "live" data structures, so this is the final mutation step
- // for the package. Which means it needs to be finalized here to cache derived fields.
- // This is relevant for cases where the disabled system package is used for flags or
- // other metadata.
- parsedPackage.hideAsFinal();
- throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
- + " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
- + " better than this " + parsedPackage.getLongVersionCode());
- }
-
- // Verify certificates against what was last scanned. Force re-collecting certificate in two
- // special cases:
- // 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scannning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
- final boolean forceCollect = scanSystemPartition ? isUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- if (DEBUG_VERIFY && forceCollect) {
- Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
- }
-
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- // TODO(b/136132412): skip for Incremental installation
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
- collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
- isPreNMR1Upgrade);
-
- // Reset profile if the application version is changed
- maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
- boolean shouldHideSystemApp = false;
- // A new application appeared on /system, but, we already have a copy of
- // the application installed on /data.
- if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
- && !pkgSetting.isSystem()) {
-
- if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.signatures.mSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.signatures.mSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.ROLLBACK)) {
- logCriticalInfo(Log.WARN,
- "System package signature mismatch;"
- + " name: " + pkgSetting.name);
- try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
- parsedPackage.getPackageName(),
- "scanPackageInternalLI")) {
- mPm.deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), 0, null, false);
- }
- pkgSetting = null;
- } else if (newPkgVersionGreater) {
- // The application on /system is newer than the application on /data.
- // Simply remove the application on /data [keeping application data]
- // and replace it with the version on /system.
- logCriticalInfo(Log.WARN,
- "System package enabled;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString() + " --> "
- + parsedPackage.getPath());
- InstallArgs args = mPm.createInstallArgsForExisting(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
- synchronized (mPm.mInstallLock) {
- args.cleanUpResourcesLI();
- }
- } else {
- // The application on /system is older than the application on /data. Hide
- // the application on /system and the version on /data will be scanned later
- // and re-added like an update.
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO,
- "System package disabled;"
- + " name: " + pkgSetting.name
- + "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.versionCode
- + "; new: " + parsedPackage.getPath() + " @ "
- + parsedPackage.getPath());
- }
- }
-
- final ScanResult scanResult = mPm.scanPackageNewLI(parsedPackage, parseFlags, scanFlags
- | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- if (scanResult.mSuccess) {
- synchronized (mPm.mLock) {
- boolean appIdCreated = false;
- try {
- final String pkgName = scanResult.mPkgSetting.name;
- final Map<String, ReconciledPackage> reconcileResult =
- mPm.reconcilePackagesLocked(
- new ReconcileRequest(
- Collections.singletonMap(pkgName, scanResult),
- mPm.mSharedLibraries,
- mPm.mPackages,
- Collections.singletonMap(
- pkgName,
- mPm.getSettingsVersionForPackage(parsedPackage)),
- Collections.singletonMap(pkgName,
- mPm.getSharedLibLatestVersionSetting(scanResult))),
- mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
- appIdCreated = mPm.optimisticallyRegisterAppId(scanResult);
- mPm.commitReconciledScanResultLocked(
- reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
- } catch (PackageManagerException e) {
- if (appIdCreated) {
- mPm.cleanUpAppIdCreation(scanResult);
- }
- throw e;
- }
- }
- }
-
- if (shouldHideSystemApp) {
- synchronized (mPm.mLock) {
- mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
- }
- }
- if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
- if (pkgSetting != null && pkgSetting.isPackageLoading()) {
- // Continue monitoring loading progress of active incremental packages
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
- pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
- mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
- new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
- }
- }
- return scanResult.mPkgSetting.pkg;
- }
-
- /**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
- */
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
- return false;
- }
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mPm.mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
- return true;
- }
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
- boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
- throws PackageManagerException {
- // When upgrading from pre-N MR1, verify the package time stamp using the package
- // directory and not the APK file.
- final long lastModifiedTime = mIsPreNMR1Upgrade
- ? new File(parsedPackage.getPath()).lastModified()
- : getLastModifiedTime(parsedPackage);
- final Settings.VersionInfo settingsVersionForPackage =
- mPm.getSettingsVersionForPackage(parsedPackage);
- if (ps != null && !forceCollect
- && ps.getPathString().equals(parsedPackage.getPath())
- && ps.timeStamp == lastModifiedTime
- && !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
- && !PackageManagerService.isRecoverSignatureUpdateNeeded(
- settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.getSignatures() != null
- && ps.signatures.mSigningDetails.getSignatures().length != 0
- && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
- != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
- // Optimization: reuse the existing cached signing data
- // if the package appears to be unchanged.
- parsedPackage.setSigningDetails(
- new SigningDetails(ps.signatures.mSigningDetails));
- return;
- }
-
- Slog.w(TAG, "PackageSetting for " + ps.name
- + " is missing signatures. Collecting certs again to recover them.");
- } else {
- Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
- + (forceCollect ? " (forced)" : ""));
- }
-
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, skipVerify);
- if (result.isError()) {
- throw new PackageManagerException(
- result.getErrorCode(), result.getErrorMessage(), result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Clear the package profile if this was an upgrade and the package
- * version was updated.
- */
- private void maybeClearProfilesForUpgradesLI(
- @Nullable PackageSetting originalPkgSetting,
- @NonNull AndroidPackage pkg) {
- if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
- return;
- }
- if (originalPkgSetting.versionCode == pkg.getLongVersionCode()) {
- return;
- }
-
- mPm.clearAppProfilesLIF(pkg);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.name
- + " clear profile due to version change "
- + originalPkgSetting.versionCode + " != "
- + pkg.getLongVersionCode());
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 4a8c76b..bc7d95e 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -28,8 +28,8 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
@@ -551,6 +551,7 @@
new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
for (InstallRequest request : requests) {
@@ -579,7 +580,7 @@
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
- final ScanResult result = mPm.scanPackageTracedLI(
+ final ScanResult result = scanPackageHelper.scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -591,7 +592,8 @@
+ " in multi-package install request.");
return;
}
- createdAppId.put(packageName, mPm.optimisticallyRegisterAppId(result));
+ createdAppId.put(packageName,
+ scanPackageHelper.optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.pkg.getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.pkg));
if (result.mStaticSharedLibraryInfo != null) {
@@ -668,7 +670,7 @@
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
false)) {
- mPm.cleanUpAppIdCreation(result);
+ scanPackageHelper.cleanUpAppIdCreation(result);
}
}
// TODO(b/194319951): create a more descriptive reason than unknown
@@ -1202,19 +1204,17 @@
}
// Check for shared user id changes
- String invalidPackageName = null;
if (!Objects.equals(oldPackage.getSharedUserId(),
parsedPackage.getSharedUserId())
// Don't mark as invalid if the app is trying to
// leave a sharedUserId
&& parsedPackage.getSharedUserId() != null) {
- invalidPackageName = parsedPackage.getPackageName();
- }
-
- if (invalidPackageName != null) {
- throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Package " + invalidPackageName + " tried to change user "
- + oldPackage.getSharedUserId());
+ throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+ "Package " + parsedPackage.getPackageName()
+ + " shared user changed from "
+ + (oldPackage.getSharedUserId() != null
+ ? oldPackage.getSharedUserId() : "<nothing>")
+ + " to " + parsedPackage.getSharedUserId());
}
// In case of rollback, remember per-user/profile install state
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b34a3a2..314cd69 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -135,6 +135,8 @@
private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50;
/** Upper bound on number of historical sessions for a UID */
private static final long MAX_HISTORICAL_SESSIONS = 1048576;
+ /** Destroy sessions older than this on storage free request */
+ private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS;
/**
* Allow verification-skipping if it's a development app installed through ADB with
@@ -339,22 +341,28 @@
@GuardedBy("mSessions")
private void reconcileStagesLocked(String volumeUuid) {
- final File stagingDir = getTmpSessionDir(volumeUuid);
- final ArraySet<File> unclaimedStages = newArraySet(
- stagingDir.listFiles(sStageFilter));
-
- // We also need to clean up orphaned staging directory for staged sessions
- final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
- unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
-
+ final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid);
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
unclaimedStages.remove(session.stageDir);
}
+ removeStagingDirs(unclaimedStages);
+ }
+ private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) {
+ final File stagingDir = getTmpSessionDir(volumeUuid);
+ final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter));
+
+ // We also need to clean up orphaned staging directory for staged sessions
+ final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
+ stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
+ return stagingDirs;
+ }
+
+ private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
// Clean up orphaned staging directories
- for (File stage : unclaimedStages) {
+ for (File stage : stagingDirsToRemove) {
Slog.w(TAG, "Deleting orphan stage " + stage);
synchronized (mPm.mInstallLock) {
mPm.removeCodePathLI(stage);
@@ -368,6 +376,33 @@
}
}
+ /**
+ * Called to free up some storage space from obsolete installation files
+ */
+ public void freeStageDirs(String volumeUuid) {
+ final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid);
+ final long currentTimeMillis = System.currentTimeMillis();
+ synchronized (mSessions) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) {
+ // Only handles sessions stored on the target volume
+ continue;
+ }
+ final long age = currentTimeMillis - session.createdMillis;
+ if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) {
+ // Aggressively close old sessions because we are running low on storage
+ // Their staging dirs will be removed too
+ session.abandon();
+ } else {
+ // Session is new enough, so it deserves to be kept even on low storage
+ unclaimedStagingDirsOnVolume.remove(session.stageDir);
+ }
+ }
+ }
+ removeStagingDirs(unclaimedStagingDirsOnVolume);
+ }
+
public static boolean isStageName(String name) {
final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ceee0fa..0f5f293 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -128,6 +128,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ExceptionUtils;
+import android.util.IntArray;
import android.util.MathUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -280,6 +281,7 @@
private final PackageInstallerService.InternalCallback mCallback;
private final Context mContext;
private final PackageManagerService mPm;
+ private final Installer mInstaller;
private final Handler mHandler;
private final PackageSessionProvider mSessionProvider;
private final SilentUpdatePolicy mSilentUpdatePolicy;
@@ -587,7 +589,7 @@
@Override
public void installSession(IntentSender statusReceiver) {
assertCallerIsOwnerOrRootOrSystem();
- assertNotChildLocked("StagedSession#installSession");
+ assertNotChild("StagedSession#installSession");
Preconditions.checkArgument(isCommitted() && isSessionReady());
// Since staged sessions are installed during boot, the original reference to status
@@ -599,12 +601,12 @@
private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
synchronized (mLock) {
- mRemoteStatusReceiver = remoteStatusReceiver;
+ setRemoteStatusReceiver(remoteStatusReceiver);
if (isMultiPackage()) {
final IntentSender childIntentSender = new ChildStatusIntentReceiver(
mChildSessions.clone(), remoteStatusReceiver).getIntentSender();
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
- mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender;
+ mChildSessions.valueAt(i).setRemoteStatusReceiver(childIntentSender);
}
}
}
@@ -684,7 +686,7 @@
public void abandon() {
final Runnable r;
synchronized (mLock) {
- assertNotChildLocked("StagedSession#abandon");
+ assertNotChild("StagedSession#abandon");
assertCallerIsOwnerOrRoot();
if (isInTerminalState()) {
// We keep the session in the database if it's in a finalized state. It will be
@@ -754,7 +756,7 @@
public void verifySession() {
assertCallerIsOwnerOrRootOrSystem();
Preconditions.checkArgument(isCommitted());
- Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+ Preconditions.checkArgument(!isInTerminalState());
notifyStartPreRebootVerification();
verify();
}
@@ -918,8 +920,10 @@
}
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
+ // It may wait for a long time to finish {@code dpmi.canSilentlyInstallPackage}.
+ // Please don't acquire mLock before calling {@code dpmi.canSilentlyInstallPackage}.
return dpmi != null && dpmi.canSilentlyInstallPackage(
- mInstallSource.installerPackageName, mInstallerUid);
+ getInstallSource().installerPackageName, mInstallerUid);
}
private static final int USER_ACTION_NOT_NEEDED = 0;
@@ -1008,8 +1012,10 @@
return USER_ACTION_REQUIRED;
}
+ @SuppressWarnings("GuardedBy" /*mPm.mInstaller is {@code final} field*/)
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
- Context context, PackageManagerService pm, PackageSessionProvider sessionProvider,
+ Context context, PackageManagerService pm,
+ PackageSessionProvider sessionProvider,
SilentUpdatePolicy silentUpdatePolicy, Looper looper, StagingManager stagingManager,
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis, long committedMillis,
@@ -1022,6 +1028,7 @@
mCallback = callback;
mContext = context;
mPm = pm;
+ mInstaller = (mPm != null) ? mPm.mInstaller : null;
mSessionProvider = sessionProvider;
mSilentUpdatePolicy = silentUpdatePolicy;
mHandler = new Handler(looper, mHandlerCallback);
@@ -1131,6 +1138,10 @@
private SessionInfo generateInfoInternal(boolean includeIcon, boolean scrubData) {
final SessionInfo info = new SessionInfo();
+ final float progress;
+ synchronized (mProgressLock) {
+ progress = mProgress;
+ }
synchronized (mLock) {
info.sessionId = sessionId;
info.userId = userId;
@@ -1138,7 +1149,7 @@
info.installerAttributionTag = mInstallSource.installerAttributionTag;
info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
mResolvedBaseFile.getAbsolutePath() : null;
- info.progress = mProgress;
+ info.progress = progress;
info.sealed = mSealed;
info.isCommitted = mCommitted.get();
info.active = mActiveCount.get() > 0;
@@ -1364,7 +1375,7 @@
return;
}
- final String initiatingPackageName = mInstallSource.initiatingPackageName;
+ final String initiatingPackageName = getInstallSource().initiatingPackageName;
final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
appOps.checkPackage(Binder.getCallingUid(), initiatingPackageName);
@@ -1585,7 +1596,7 @@
if (params.sizeBytes > 0) {
final long delta = progress - last.value;
last.value = progress;
- synchronized (mLock) {
+ synchronized (mProgressLock) {
setClientProgressLocked(mClientProgress
+ (float) delta / (float) params.sizeBytes);
}
@@ -1897,13 +1908,6 @@
}
}
- /** {@hide} */
- private class StreamingException extends Exception {
- StreamingException(Throwable cause) {
- super(cause);
- }
- }
-
/**
* Returns whether or not a package can be installed while Secure FRP is enabled.
* <p>
@@ -1973,7 +1977,7 @@
}
}
- mRemoteStatusReceiver = statusReceiver;
+ setRemoteStatusReceiver(statusReceiver);
// After updating the observer, we can skip re-sealing.
if (mSealed) {
@@ -2140,7 +2144,7 @@
private void onSystemDataLoaderUnrecoverable() {
final PackageManagerService packageManagerService = mPm;
- final String packageName = mPackageName;
+ final String packageName = getPackageName();
if (TextUtils.isEmpty(packageName)) {
// The package has not been installed.
return;
@@ -2303,9 +2307,7 @@
*/
@WorkerThread
private boolean sendPendingUserActionIntentIfNeeded() {
- synchronized (mLock) {
- assertNotChildLocked("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
- }
+ assertNotChild("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
return sessionContains(PackageInstallerSession::checkUserActionRequirement);
}
@@ -2315,7 +2317,7 @@
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
- .setAdmin(mInstallSource.installerPackageName)
+ .setAdmin(getInstallSource().installerPackageName)
.write();
}
@@ -2392,6 +2394,12 @@
}
}
+ private void setRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
+ synchronized (mLock) {
+ mRemoteStatusReceiver = remoteStatusReceiver;
+ }
+ }
+
private void verifyNonStaged()
throws PackageManagerException {
final VerificationParams verifyingSession = prepareForVerification();
@@ -2882,7 +2890,7 @@
final List<File> addedFiles = getAddedApksLocked();
if (addedFiles.isEmpty()) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- String.format("Session: %d. No packages staged in %s", sessionId,
+ TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
stageDir.getAbsolutePath()));
}
@@ -2982,7 +2990,7 @@
final List<File> addedFiles = getAddedApksLocked();
if (addedFiles.isEmpty() && removeSplitList.size() == 0) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- String.format("Session: %d. No packages staged in %s", sessionId,
+ TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
stageDir.getAbsolutePath()));
}
@@ -3316,12 +3324,19 @@
DexMetadataHelper.isFsVerityRequired());
}
+ private IncrementalFileStorages getIncrementalFileStorages() {
+ synchronized (mLock) {
+ return mIncrementalFileStorages;
+ }
+ }
+
private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
final byte[] bytes) throws IOException {
- if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+ final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
+ if (!isIncrementalInstallation() || incrementalFileStorages == null) {
FileUtils.bytesToFile(absolutePath, bytes);
} else {
- mIncrementalFileStorages.makeFile(localPath, bytes);
+ incrementalFileStorages.makeFile(localPath, bytes);
}
}
@@ -3583,7 +3598,7 @@
throws PackageManagerException {
for (String instructionSet : instructionSets) {
try {
- mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
} catch (InstallerException e) {
throw PackageManagerException.from(e);
}
@@ -3593,11 +3608,12 @@
private void linkFile(String relativePath, String fromBase, String toBase) throws IOException {
try {
// Try
- if (mIncrementalFileStorages != null && mIncrementalFileStorages.makeLink(relativePath,
+ final IncrementalFileStorages incrementalFileStorages = getIncrementalFileStorages();
+ if (incrementalFileStorages != null && incrementalFileStorages.makeLink(relativePath,
fromBase, toBase)) {
return;
}
- mPm.mInstaller.linkFile(relativePath, fromBase, toBase);
+ mInstaller.linkFile(relativePath, fromBase, toBase);
} catch (InstallerException | IOException e) {
throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
+ fromBase + ", " + toBase + ")", e);
@@ -3767,7 +3783,7 @@
private void abandonNonStaged() {
synchronized (mLock) {
- assertNotChildLocked("abandonNonStaged");
+ assertNotChild("abandonNonStaged");
assertCallerIsOwnerOrRoot();
if (mRelinquished) {
if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
@@ -3779,8 +3795,7 @@
maybeCleanUpChildSessions();
}
- @GuardedBy("mLock")
- private void assertNotChildLocked(String cookie) {
+ private void assertNotChild(String cookie) {
if (hasParentSessionId()) {
throw new IllegalStateException(cookie + " can't be called on a child session, id="
+ sessionId + " parentId=" + getParentSessionId());
@@ -4324,7 +4339,7 @@
if (incrementalFileStorages != null) {
incrementalFileStorages.cleanUpAndMarkComplete();
}
- mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
}
}
@@ -4350,7 +4365,7 @@
if (incrementalFileStorages != null) {
incrementalFileStorages.cleanUpAndMarkComplete();
}
- mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
}
}
@@ -4382,8 +4397,14 @@
params.dump(pw);
- pw.printPair("mClientProgress", mClientProgress);
- pw.printPair("mProgress", mProgress);
+ final float clientProgress;
+ final float progress;
+ synchronized (mProgressLock) {
+ clientProgress = mClientProgress;
+ progress = mProgress;
+ }
+ pw.printPair("mClientProgress", clientProgress);
+ pw.printPair("mProgress", progress);
pw.printPair("mCommitted", mCommitted);
pw.printPair("mSealed", mSealed);
pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
@@ -4693,7 +4714,7 @@
* @param installerThread Thread to be used for callbacks of this session
* @param sessionsDir The directory the sessions are stored in
*
- * @param sessionProvider
+ * @param sessionProvider to get the other PackageInstallerSession instance by sessionId.
* @return The newly created session
*/
public static PackageInstallerSession readFromXml(@NonNull TypedXmlPullParser in,
@@ -4781,7 +4802,7 @@
List<String> grantedRuntimePermissions = new ArrayList<>();
List<String> whitelistedRestrictedPermissions = new ArrayList<>();
int autoRevokePermissionsMode = MODE_DEFAULT;
- List<Integer> childSessionIds = new ArrayList<>();
+ IntArray childSessionIds = new IntArray();
List<InstallationFile> files = new ArrayList<>();
ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>();
ArrayMap<String, byte[]> signatures = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index de51128..e62102c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -32,16 +32,10 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -70,7 +64,6 @@
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -89,12 +82,8 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
-import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
@@ -114,7 +103,6 @@
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.PendingIntent;
-import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
@@ -209,7 +197,6 @@
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.result.ParseResult;
@@ -250,7 +237,6 @@
import android.os.incremental.PerUidReadTimeouts;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
-import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
@@ -288,8 +274,6 @@
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
-import android.util.apk.ApkSignatureVerifier;
-import android.util.jar.StrictJarFile;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -302,7 +286,6 @@
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.AttributeCache;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
@@ -341,7 +324,6 @@
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -353,7 +335,6 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateImpl;
-import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
@@ -786,7 +767,7 @@
@VisibleForTesting
final File mAppLib32InstallDir;
- private static File getAppLib32InstallDir() {
+ static File getAppLib32InstallDir() {
return new File(Environment.getDataDirectory(), "app-lib");
}
@@ -923,7 +904,7 @@
private final List<ScanPartition> mDirsToScanAsSystem;
- private final OverlayConfig mOverlayConfig;
+ final OverlayConfig mOverlayConfig;
@GuardedBy("itself")
final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
@@ -1416,7 +1397,7 @@
new WatchedSparseBooleanArray();
@Watched(manual = true)
- private ApplicationInfo mAndroidApplication;
+ ApplicationInfo mAndroidApplication;
@Watched(manual = true)
final ActivityInfo mResolveActivity = new ActivityInfo();
final ResolveInfo mResolveInfo = new ResolveInfo();
@@ -1448,7 +1429,7 @@
final PermissionManagerServiceInternal mPermissionManager;
@Watched
- private final ComponentResolver mComponentResolver;
+ final ComponentResolver mComponentResolver;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -6248,58 +6229,6 @@
return StorageEnums.UNKNOWN;
}
- private final StorageEventListener mStorageListener = new StorageEventListener() {
- @Override
- public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
- if (vol.type == VolumeInfo.TYPE_PRIVATE) {
- if (vol.state == VolumeInfo.STATE_MOUNTED) {
- final String volumeUuid = vol.getFsUuid();
-
- // Clean up any users or apps that were removed or recreated
- // while this volume was missing
- mUserManager.reconcileUsers(volumeUuid);
- reconcileApps(volumeUuid);
-
- // Clean up any install sessions that expired or were
- // cancelled while this volume was missing
- mInstallerService.onPrivateVolumeMounted(volumeUuid);
-
- loadPrivatePackages(vol);
-
- } else if (vol.state == VolumeInfo.STATE_EJECTING) {
- unloadPrivatePackages(vol);
- }
- }
- }
-
- @Override
- public void onVolumeForgotten(String fsUuid) {
- if (TextUtils.isEmpty(fsUuid)) {
- Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
- return;
- }
-
- // Remove any apps installed on the forgotten volume
- synchronized (mLock) {
- final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid);
- for (PackageSetting ps : packages) {
- Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
- deletePackageVersioned(new VersionedPackage(ps.name,
- PackageManager.VERSION_CODE_HIGHEST),
- new LegacyPackageDeleteObserver(null).getBinder(),
- UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
- // Try very hard to release any references to this package
- // so we don't risk the system server being killed due to
- // open FDs
- AttributeCache.instance().removePackage(ps.name);
- }
-
- mSettings.onVolumeForgotten(fsUuid);
- writeSettingsLPrTEMP();
- }
- }
- };
-
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
switch (res.mReturnCode) {
@@ -6963,8 +6892,9 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/,
- mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
+ ScanPackageHelper.applyAdjustedAbiToSharedUser(
+ setting, null /*scannedPackage*/,
+ mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
@@ -7230,11 +7160,6 @@
Slog.i(TAG, "Fix for b/169414761 is applied");
}
-
-
-
-
-
@GuardedBy("mLock")
void updateInstantAppInstallerLocked(String modifiedPackage) {
// we're only interested in updating the installer appliction when 1) it's not
@@ -8079,6 +8004,9 @@
if (freeBytesRequired > 0) {
smInternal.freeCache(volumeUuid, freeBytesRequired);
}
+
+ // 12. Clear temp install session files
+ mInstallerService.freeStageDirs(volumeUuid);
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);
@@ -10750,52 +10678,6 @@
logCriticalInfo(priority, msg);
}
-
-
-
-
- /**
- * Traces a package scan.
- * @see #scanPackageLI(File, int, int, long, UserHandle)
- */
- @GuardedBy({"mInstallLock", "mLock"})
- AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
- try {
- return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Scans a package and returns the newly parsed package.
- * Returns {@code null} in case of errors and the error code is stored in mLastScanError
- */
- @GuardedBy({"mInstallLock", "mLock"})
- private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
- if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
- parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Static shared libraries have synthetic package names
- if (parsedPackage.isStaticSharedLibrary()) {
- renameStaticSharedLibraryPackage(parsedPackage);
- }
-
- final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
- return helper.addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- mPlatformPackage, mIsUpgrade, mIsPreNMR1Upgrade);
- }
-
// TODO:(b/135203078): Move to parsing
static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
// Derive the new package synthetic package name
@@ -11630,22 +11512,6 @@
}
}
- @GuardedBy("mLock")
- private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
- + " to " + newPkg.getPackageName()
- + ": old package not in system partition");
- return false;
- } else if (mPackages.get(oldPkg.name) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
- + " to " + newPkg.getPackageName()
- + ": old package still exists");
- return false;
- }
- return true;
- }
-
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
if (codePath.isDirectory()) {
@@ -12106,221 +11972,6 @@
return resultList;
}
- private int getVendorPartitionVersion() {
- final String version = SystemProperties.get("ro.vndk.version");
- if (!version.isEmpty()) {
- try {
- return Integer.parseInt(version);
- } catch (NumberFormatException ignore) {
- if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
- }
- }
- return Build.VERSION_CODES.P;
- }
-
- @GuardedBy({"mInstallLock", "mLock"})
- ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
- final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
- try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- cpuAbiOverride);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Returns the actual scan flags depending upon the state of the other settings.
- * <p>Updated system applications will not have the following flags set
- * by default and need to be adjusted after the fact:
- * <ul>
- * <li>{@link #SCAN_AS_SYSTEM}</li>
- * <li>{@link #SCAN_AS_PRIVILEGED}</li>
- * <li>{@link #SCAN_AS_OEM}</li>
- * <li>{@link #SCAN_AS_VENDOR}</li>
- * <li>{@link #SCAN_AS_PRODUCT}</li>
- * <li>{@link #SCAN_AS_SYSTEM_EXT}</li>
- * <li>{@link #SCAN_AS_INSTANT_APP}</li>
- * <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li>
- * <li>{@link #SCAN_AS_ODM}</li>
- * </ul>
- */
- private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
- PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
- AndroidPackage pkg) {
-
- // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
- // the correct isSystem value now that we don't disable system packages before scan.
- final PackageSetting systemPkgSetting =
- (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
- && pkgSetting != null && pkgSetting.isSystem()
- ? pkgSetting
- : disabledPkgSetting;
- if (systemPkgSetting != null) {
- // updated system application, must at least have SCAN_AS_SYSTEM
- scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if ((systemPkgSetting.pkgPrivateFlags
- & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
- scanFlags |= SCAN_AS_ODM;
- }
- }
- if (pkgSetting != null) {
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (pkgSetting.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (pkgSetting.getVirtulalPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
- }
-
- // Scan as privileged apps that share a user with a priv-app.
- final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
- && getVendorPartitionVersion() < 28;
- if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
- && !pkg.isPrivileged()
- && (pkg.getSharedUserId() != null)
- && !skipVendorPrivilegeScan) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
- 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- // TODO(b/72378145) Fix this exemption. Force signature apps
- // to allowlist their privileged permissions just like other
- // priv-apps.
- synchronized (mLock) {
- PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.signatures.mSigningDetails.getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- }
- }
- }
-
- return scanFlags;
- }
-
- // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting
- // the results / removing app data needs to be moved up a level to the callers of this
- // method. Also, we need to solve the problem of potentially creating a new shared user
- // setting. That can probably be done later and patch things up after the fact.
- @GuardedBy({"mInstallLock", "mLock"})
- ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
- final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
- final String renamedPkgName = mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());
- final PackageSetting disabledPkgSetting =
- mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
-
- if (mTransferredPackages.contains(parsedPackage.getPackageName())) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " was transferred to another, but its .apk remains");
- }
-
- scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
- synchronized (mLock) {
- boolean isUpdatedSystemApp;
- if (pkgSetting != null) {
- isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
- } else {
- isUpdatedSystemApp = disabledPkgSetting != null;
- }
- applyPolicy(parsedPackage, scanFlags, mPlatformPackage, isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
-
- SharedUserSetting sharedUserSetting = null;
- if (parsedPackage.getSharedUserId() != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
- }
- }
- String platformPackageName = mPlatformPackage == null
- ? null : mPlatformPackage.getPackageName();
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
- cpuAbiOverride);
- return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
- }
- }
-
- /**
- * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
- * the app ID required for reconcile.
- * @return {@code true} if a new app ID was registered and will need to be cleaned up on
- * failure.
- */
- boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
- throws PackageManagerException {
- if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- return mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
- }
- return false;
- }
-
- /**
- * Reverts any app ID creation that were made by
- * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
- * referenced method returned true.
- */
- void cleanUpAppIdCreation(@NonNull ScanResult result) {
- // iff we've acquired an app ID for a new package setting, remove it so that it can be
- // acquired by another request.
- if (result.mPkgSetting.appId > 0) {
- mSettings.removeAppIdLPw(result.mPkgSetting.appId);
- }
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an excpetion in the middle
@@ -12445,1024 +12096,6 @@
return pkg;
}
- /**
- * Returns the "real" name of the package.
- * <p>This may differ from the package's actual name if the application has already
- * been installed under one of this package's original names.
- */
- static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return AndroidPackageUtils.getRealPackageOrNull(pkg);
- }
- return null;
- }
-
- /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
- private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- return pkg.getOriginalPackages().contains(renamedPkgName);
- }
-
- /**
- * Returns the original package setting.
- * <p>A package can migrate its name during an update. In this scenario, a package
- * designates a set of names that it considers as one of its original names.
- * <p>An original package must be signed identically and it must have the same
- * shared user [if any].
- */
- @GuardedBy("mLock")
- @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return null;
- }
- for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
- final PackageSetting originalPs =
- mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
- if (originalPs != null) {
- // the package is already installed under its original name...
- // but, should we use it?
- if (!verifyPackageUpdateLPr(originalPs, pkg)) {
- // the new package is incompatible with the original
- continue;
- } else if (originalPs.sharedUser != null) {
- if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
- // the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.name
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.sharedUser.name
- + " differs from " + pkg.getSharedUserId());
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name " + originalPs.name);
- }
- return originalPs;
- }
- }
- return null;
- }
-
- /**
- * Renames the package if it was installed under a different name.
- * <p>When we've already installed the package under an original name, update
- * the new package so we can continue to have the old name.
- */
- static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
- @NonNull String renamedPackageName) {
- if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
- || parsedPackage.getPackageName().equals(renamedPackageName)) {
- return;
- }
- parsedPackage.setPackageName(renamedPackageName);
- }
-
- /**
- * Applies the adjusted ABI calculated by
- * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
- * relevant packages and settings.
- * @param sharedUserSetting The {@code SharedUserSetting} to adjust
- * @param scannedPackage the package being scanned or null
- * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
- * @return the list of code paths that belong to packages that had their ABIs adjusted.
- */
- private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
- ParsedPackage scannedPackage, String adjustedAbi) {
- if (scannedPackage != null) {
- scannedPackage.setPrimaryCpuAbi(adjustedAbi);
- }
- List<String> changedAbiCodePath = null;
- for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
- if (ps.primaryCpuAbiString != null) {
- continue;
- }
-
- ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null) {
- if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.getPathString());
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
- /**
- * Sets the enabled state of components configured through {@link SystemConfig}.
- * This modifies the {@link PackageSetting} object.
- *
- * TODO(b/135203078): Move this to package parsing
- **/
- static void configurePackageComponents(AndroidPackage pkg) {
- final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
- .getComponentsEnabledStates(pkg.getPackageName());
- if (componentsEnabledStates == null) {
- return;
- }
-
- for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
- final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
- final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- component.setEnabled(enabled);
- }
- }
- }
-
- /**
- * Just scans the package without any side effects.
- * <p>Not entirely true at the moment. There is still one side effect -- this
- * method potentially modifies a live {@link PackageSetting} object representing
- * the package being scanned. This will be resolved in the future.
- *
- * @param injector injector for acquiring dependencies
- * @param request Information about the package to be scanned
- * @param isUnderFactoryTest Whether or not the device is under factory test
- * @param currentTime The current time, in millis
- * @return The results of the scan
- */
- @GuardedBy("mInstallLock")
- @VisibleForTesting
- @NonNull
- ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- Injector injector,
- boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
- final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- ParsedPackage parsedPackage = request.mParsedPackage;
- PackageSetting pkgSetting = request.mPkgSetting;
- final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
- final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
- final @ParseFlags int parseFlags = request.mParseFlags;
- final @ScanFlags int scanFlags = request.mScanFlags;
- final String realPkgName = request.mRealPkgName;
- final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
- final UserHandle user = request.mUser;
- final boolean isPlatformPackage = request.mIsPlatformPackage;
-
- List<String> changedAbiCodePath = null;
-
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
- }
- }
-
- // Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getPath());
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if (!needToDeriveAbi) {
- if (pkgSetting != null) {
- // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
- // API info from existing package setting. However, stub packages currently do not
- // preserve ABI info, thus the special condition check here. Remove the special
- // check after we fix the stub generation.
- if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
- needToDeriveAbi = true;
- } else {
- primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
- secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
- }
- } else {
- // Re-scanning a system package after uninstalling updates; need to derive ABI
- needToDeriveAbi = true;
- }
- }
-
- boolean leavingSharedUser = false;
-
- if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
- if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
- leavingSharedUser = true;
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.sharedUser.name + " to " + "<nothing>.");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.sharedUser != null
- ? pkgSetting.sharedUser.name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
- }
-
- String[] usesStaticLibraries = null;
- if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
- usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
- parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
- }
-
- final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
-
- // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
- // to avoid adding something that's unsupported due to lack of state, since it's called
- // with null.
- final boolean createNewPackage = (pkgSetting == null);
- if (createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-
- // Flags contain system values stored in the server variant of AndroidPackage,
- // and so the server-side PackageInfoUtils is still called, even without a
- // PackageSetting to pass in.
- int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
- int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
-
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
- originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryRootDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
- } else {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.pkg = parsedPackage;
-
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
- UserManagerService.getInstance(),
- usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
- }
- if (createNewPackage && originalPkgSetting != null) {
- // This is the initial transition from the original package, so,
- // fix up the new package's name now. We must do this after looking
- // up the package under its new name, so getPackageLP takes care of
- // fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.name);
-
- // File a report about this.
- String msg = "New package " + pkgSetting.realName
- + " renamed to replace old package " + pkgSetting.name;
- reportSettingsProblem(Log.WARN, msg);
- }
-
- final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
- // for existing packages, change the install state; but, only if it's explicitly specified
- if (!createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
- }
- // TODO(patb): see if we can do away with disabled check here.
- if (disabledPkgSetting != null
- || (0 != (scanFlags & SCAN_NEW_INSTALL)
- && pkgSetting != null && pkgSetting.isSystem())) {
- pkgSetting.getPkgState().setUpdatedSystemApp(true);
- }
-
- parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
- injector.getCompatibility()));
-
- if (parsedPackage.isSystem()) {
- configurePackageComponents(parsedPackage);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
- final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
-
- final File appLib32InstallDir = getAppLib32InstallDir();
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride, appLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
-
- String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !isUpdatedSystemApp
- && pkgRawPrimaryCpuAbi == null) {
- final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
- parsedPackage);
- abis.applyTo(parsedPackage);
- abis.applyTo(pkgSetting);
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
- .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
-
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
- parsedPackage.getPackageName() + " " +
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
- + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
- .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (isPlatformPackage) {
- parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?
- Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null) {
- Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
- " for package " + parsedPackage.getPackageName());
- }
- }
-
- pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
- pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir()
- + ", to dir=" + parsedPackage.getNativeLibraryDir()
- + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.primaryCpuAbiString
- + " secondary=" + pkgSetting.primaryCpuAbiString
- + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
- packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.sharedUser.packages, parsedPackage));
- }
-
- parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
- .contains(android.Manifest.permission.FACTORY_TEST));
-
- if (parsedPackage.isSystem()) {
- pkgSetting.setIsOrphaned(true);
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(parsedPackage);
- if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
- }
- } else if (pkgSetting.firstInstallTime == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
- } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.timeStamp) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.lastUpdateTime = scanFileTime;
- }
- }
- pkgSetting.setTimeStamp(scanFileTime);
- // TODO(b/135203078): Remove, move to constructor
- pkgSetting.pkg = parsedPackage;
- pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
- pkgSetting.pkgPrivateFlags =
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
- if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
- pkgSetting.versionCode = parsedPackage.getLongVersionCode();
- }
- // Update volume if needed
- final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
- Slog.i(PackageManagerService.TAG,
- "Update" + (pkgSetting.isSystem() ? " system" : "")
- + " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.volumeUuid
- + " to " + volumeUuid);
- pkgSetting.volumeUuid = volumeUuid;
- }
-
- SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo =
- AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
- }
- List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
- if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
- dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
- for (String name : parsedPackage.getLibraryNames()) {
- dynamicSharedLibraryInfos.add(
- AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
- }
- }
-
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */,
- leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
- }
-
- /**
- * Returns {@code true} if the given file contains code. Otherwise {@code false}.
- */
- private static boolean apkHasCode(String fileName) {
- StrictJarFile jarFile = null;
- try {
- jarFile = new StrictJarFile(fileName,
- false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
- return jarFile.findEntry("classes.dex") != null;
- } catch (IOException ignore) {
- } finally {
- try {
- if (jarFile != null) {
- jarFile.close();
- }
- } catch (IOException ignore) {}
- }
- return false;
- }
-
- /**
- * Enforces code policy for the package. This ensures that if an APK has
- * declared hasCode="true" in its manifest that the APK actually contains
- * code.
- *
- * @throws PackageManagerException If bytecode could not be found when it should exist
- */
- private static void assertCodePolicy(AndroidPackage pkg)
- throws PackageManagerException {
- final boolean shouldHaveCode = pkg.isHasCode();
- if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getBaseApkPath() + " code is missing");
- }
-
- if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
- for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
- final boolean splitShouldHaveCode =
- (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
- if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
- }
- }
- }
- }
-
- /**
- * Applies policy to the parsed package based upon the given policy flags.
- * Ensures the package is in a good state.
- * <p>
- * Implementation detail: This method must NOT have any side effect. It would
- * ideally be static, but, it requires locks to read system state.
- */
- static void applyPolicy(ParsedPackage parsedPackage,
- final @ScanFlags int scanFlags, AndroidPackage platformPkg,
- boolean isUpdatedSystemApp) {
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- parsedPackage.setSystem(true);
- // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
- // is set during parse.
- if (parsedPackage.isDirectBootAware()) {
- parsedPackage.setAllComponentsDirectBootAware(true);
- }
- if (compressedFileExists(parsedPackage.getPath())) {
- parsedPackage.setStub(true);
- }
- } else {
- parsedPackage
- // Non system apps cannot mark any broadcast as protected
- .clearProtectedBroadcasts()
- // non system apps can't be flagged as core
- .setCoreApp(false)
- // clear flags not applicable to regular apps
- .setPersistent(false)
- .setDefaultToDeviceProtectedStorage(false)
- .setDirectBootAware(false)
- // non system apps can't have permission priority
- .capPermissionPriorities();
- }
- if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
- parsedPackage
- .markNotActivitiesAsNotExportedIfSingleUser();
- }
-
- parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
- .setOem((scanFlags & SCAN_AS_OEM) != 0)
- .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
- .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
- .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
- .setOdm((scanFlags & SCAN_AS_ODM) != 0);
-
- // Check if the package is signed with the same key as the platform package.
- parsedPackage.setSignedWithPlatformKey(
- (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
- || (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
- ) == PackageManager.SIGNATURE_MATCH))
- );
-
- if (!parsedPackage.isSystem()) {
- // Only system apps can use these features.
- parsedPackage.clearOriginalPackages()
- .clearAdoptPermissions();
- }
-
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
- }
-
- private <T extends ParsedMainComponent>
- void assertPackageProcesses(AndroidPackage pkg, List<T> components,
- Map<String, ParsedProcess> procs, String compName)
- throws PackageManagerException {
- if (components == null) {
- return;
- }
- for (int i = components.size() - 1; i >= 0; i--) {
- final ParsedMainComponent component = components.get(i);
- if (!procs.containsKey(component.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.getClassName()
- + "'s process attribute " + component.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- }
- }
-
- /**
- * Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackageManagerException}.
- * <p>
- * Implementation detail: This method must NOT have any side effects. It would
- * ideally be static, but, it requires locks to read system state.
- *
- * @throws PackageManagerException If the package fails any of the validation checks
- */
- private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags)
- throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
- assertCodePolicy(pkg);
- }
-
- if (pkg.getPath() == null) {
- // Bail out. The resource and code paths haven't been set.
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Code and resource paths haven't been set correctly");
- }
-
- // Check that there is an APEX package with the same name only during install/first boot
- // after OTA.
- final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
- final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if ((isUserInstall || isFirstBootOrUpgrade)
- && mApexManager.isApexPackage(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
- }
-
- // Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- ksms.assertScannedPackageValid(pkg);
-
- synchronized (mLock) {
- // The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
- if (mAndroidApplication != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " codePath=" + pkg.getPath());
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
- }
-
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
- }
-
- if (pkg.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // but we still want the base name to be unique.
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPackages.containsKey(pkg.getManifestPackageName())) {
- throw new PackageManagerException(
- "Duplicate static shared lib provider package");
- }
-
- // Static shared libraries should have at least O target SDK
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs must target O SDK or higher");
- }
-
- // Package declaring static a shared lib cannot be instant apps
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be instant apps");
- }
-
- // Package declaring static a shared lib cannot be renamed since the package
- // name is synthetic and apps can't code around package manager internals.
- if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be renamed");
- }
-
- // Package declaring static a shared lib cannot declare dynamic libs
- if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare dynamic libs");
- }
-
- // Package declaring static a shared lib cannot declare shared users
- if (pkg.getSharedUserId() != null) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare shared users");
- }
-
- // Static shared libs cannot declare activities
- if (!pkg.getActivities().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare activities");
- }
-
- // Static shared libs cannot declare services
- if (!pkg.getServices().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare services");
- }
-
- // Static shared libs cannot declare providers
- if (!pkg.getProviders().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare content providers");
- }
-
- // Static shared libs cannot declare receivers
- if (!pkg.getReceivers().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare broadcast receivers");
- }
-
- // Static shared libs cannot declare permission groups
- if (!pkg.getPermissionGroups().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permission groups");
- }
-
- // Static shared libs cannot declare attributions
- if (!pkg.getAttributions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare features");
- }
-
- // Static shared libs cannot declare permissions
- if (!pkg.getPermissions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permissions");
- }
-
- // Static shared libs cannot declare protected broadcasts
- if (!pkg.getProtectedBroadcasts().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare protected broadcasts");
- }
-
- // Static shared libs cannot be overlay targets
- if (pkg.getOverlayTarget() != null) {
- throw new PackageManagerException(
- "Static shared libs cannot be overlay targets");
- }
-
- // The version codes must be ordered as lib versions
- long minVersionCode = Long.MIN_VALUE;
- long maxVersionCode = Long.MAX_VALUE;
-
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib != null) {
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(i);
- final long libVersionCode = libInfo.getDeclaringPackage()
- .getLongVersionCode();
- if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
- minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
- } else if (libInfo.getLongVersion()
- > pkg.getStaticSharedLibVersion()) {
- maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
- } else {
- minVersionCode = maxVersionCode = libVersionCode;
- break;
- }
- }
- }
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
- throw new PackageManagerException("Static shared"
- + " lib version codes must be ordered as lib versions");
- }
- }
-
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mExpectingBetter.containsKey(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
- } else {
- PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.getPath()
- + " and requiring known path " + known.getPathString());
- }
- if (!pkg.getPath().equals(known.getPathString())) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
- + " found at " + pkg.getPath()
- + " but expected at " + known.getPathString()
- + "; ignoring.");
- }
- } else {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
- + " not found; ignoring.");
- }
- }
- }
-
- // Verify that this new package doesn't have any content providers
- // that conflict with existing packages. Only do this if the
- // package isn't already installed, since we don't want to break
- // things that are installed.
- if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
- mComponentResolver.assertProvidersNotDefined(pkg);
- }
-
- // If this package has defined explicit processes, then ensure that these are
- // the only processes used by its components.
- final Map<String, ParsedProcess> procs = pkg.getProcesses();
- if (!procs.isEmpty()) {
- if (!procs.containsKey(pkg.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because application tag's process attribute "
- + pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
- assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
- assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
- assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
- }
-
- // Verify that packages sharing a user with a privileged app are marked as privileged.
- if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(),
- 0, 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Apps that share a user with a " +
- "privileged app must themselves be marked as privileged. " +
- pkg.getPackageName() + " shares privileged user " +
- pkg.getSharedUserId() + ".");
- }
- }
- }
-
- // Apply policies specific for runtime resource overlays (RROs).
- if (pkg.getOverlayTarget() != null) {
- // System overlays have some restrictions on their use of the 'static' state.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // We are scanning a system overlay. This can be the first scan of the
- // system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay. Immutable overlays cannot be
- // upgraded.
- Objects.requireNonNull(mOverlayConfig,
- "Parsing non-system dir before overlay configs are initialized");
- if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static and cannot be upgraded.");
- }
- } else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
- if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of vendor"
- + " overlays (" + getVendorPartitionVersion() + ")."
- + " This will become an install error in a future release");
- }
- } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of system"
- + " overlays (" + Build.VERSION.SDK_INT + ")."
- + " This will become an install error in a future release");
- }
- }
- } else {
- // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
- // signed with the platform certificate. Check this in increasing order of
- // computational cost.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
- final PackageSetting platformPkgSetting =
- mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " must target Q or later, "
- + "or be signed with the platform certificate");
- }
- }
-
- // A non-preloaded overlay package, without <overlay android:targetName>, will
- // only be used if it is signed with the same certificate as its target OR if
- // it is signed with the same certificate as a reference package declared
- // in 'overlay-config-signature' tag of SystemConfig.
- // If the target is already installed or 'overlay-config-signature' tag in
- // SystemConfig is set, check this here to augment the last line of defense
- // which is OMS.
- if (pkg.getOverlayTargetOverlayableName() == null) {
- final PackageSetting targetPkgSetting =
- mSettings.getPackageLPr(pkg.getOverlayTarget());
- if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- // check reference signature
- if (mOverlayConfigSignaturePackage == null) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
- + pkg.getOverlayTarget() + " signed with"
- + " different certificates, and the overlay lacks"
- + " <overlay android:targetName>");
- }
- final PackageSetting refPkgSetting =
- mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
- + "certificate than both the reference package and "
- + "target " + pkg.getOverlayTarget() + ", and the "
- + "overlay lacks <overlay android:targetName>");
- }
- }
- }
- }
- }
- }
-
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
- }
- }
- }
-
@GuardedBy("mLock")
private void addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
if (nonStaticSharedLibExistsLocked(entry.name)) {
@@ -16302,6 +14935,7 @@
int[] allUsers;
final int freezeUser;
final SparseArray<TempUserState> priorUserStates;
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
/** enabled state of the uninstalled application */
synchronized (mLock) {
uninstalledPs = mSettings.getPackageLPr(packageName);
@@ -16436,8 +15070,6 @@
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- final InitAndSystemPackageHelper helper =
- new InitAndSystemPackageHelper(this);
helper.enableCompressedPackage(stubPkg, stubPs, mDefParseFlags,
mDirsToScanAsSystem);
} else if (DEBUG_COMPRESSION) {
@@ -18445,6 +17077,7 @@
}
}
+ final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
// More work for application enabled setting updates
for (int i = 0; i < targetSize; i++) {
final ComponentEnabledSetting setting = settings.get(i);
@@ -18470,7 +17103,6 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
if (!helper.enableCompressedPackage(deletedPkg, pkgSetting, mDefParseFlags,
mDirsToScanAsSystem)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
@@ -19014,15 +17646,16 @@
mUserManager.systemReady();
// Watch for external volumes that come and go over time
+ final StorageEventHelper storageEventHelper = new StorageEventHelper(this);
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
- storage.registerListener(mStorageListener);
+ storage.registerListener(storageEventHelper);
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
// Now that we're mostly running, clean up stale users and apps
mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
- reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
+ storageEventHelper.reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
mPermissionManager.onSystemReady();
@@ -19202,6 +17835,7 @@
pw.println(" compiler-stats: dump compiler statistics");
pw.println(" service-permissions: dump permissions required by services");
pw.println(" snapshot: dump snapshot statistics");
+ pw.println(" protected-broadcasts: print list of protected broadcast actions");
pw.println(" known-packages: dump known packages");
pw.println(" <package.name>: info about given package");
return;
@@ -19361,6 +17995,8 @@
opti++;
}
}
+ } else if ("protected-broadcasts".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_PROTECTED_BROADCASTS);
} else if ("write".equals(cmd)) {
synchronized (mLock) {
writeSettingsLPrTEMP();
@@ -19762,6 +18398,22 @@
mSnapshotStatistics.dump(pw, " ", now, hits, level, dumpState.isBrief());
}
}
+
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_PROTECTED_BROADCASTS)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Protected broadcast actions:");
+ synchronized (mProtectedBroadcasts) {
+ for (int i = 0; i < mProtectedBroadcasts.size(); i++) {
+ pw.print(" ");
+ pw.println(mProtectedBroadcasts.valueAt(i));
+ }
+ }
+
+ }
}
/**
@@ -19873,29 +18525,13 @@
// ------- apps on sdcard specific code -------
static final boolean DEBUG_SD_INSTALL = false;
- private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
-
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
- final int size = packages.size();
- final String[] packageNames = new String[size];
- final int[] packageUids = new int[size];
- for (int i = 0; i < size; i++) {
- final AndroidPackage pkg = packages.get(i);
- packageNames[i] = pkg.getPackageName();
- packageUids[i] = pkg.getUid();
- }
- sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
- finishedReceiver);
- }
-
void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
sendResourcesChangedBroadcast(mediaStatus, replacing,
pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
int size = pkgList.length;
if (size > 0) {
@@ -19916,158 +18552,6 @@
}
}
- private void loadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(() -> loadPrivatePackagesInner(vol));
- }
-
- private void loadPrivatePackagesInner(VolumeInfo vol) {
- final String volumeUuid = vol.fsUuid;
- if (TextUtils.isEmpty(volumeUuid)) {
- Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
- return;
- }
-
- final ArrayList<PackageFreezer> freezers = new ArrayList<>();
- final ArrayList<AndroidPackage> loaded = new ArrayList<>();
- final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
-
- final VersionInfo ver;
- final List<PackageSetting> packages;
- synchronized (mLock) {
- ver = mSettings.findOrCreateVersion(volumeUuid);
- packages = mSettings.getVolumePackagesLPr(volumeUuid);
- }
-
- for (PackageSetting ps : packages) {
- freezers.add(freezePackage(ps.name, "loadPrivatePackagesInner"));
- synchronized (mInstallLock) {
- final AndroidPackage pkg;
- try {
- pkg = scanPackageTracedLI(ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
- loaded.add(pkg);
-
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
- }
-
- if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
- clearAppDataLIF(ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
- | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
- | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
- }
- }
- }
-
- // Reconcile app data for all started/unlocked users
- final StorageManager sm = mInjector.getSystemService(StorageManager.class);
- UserManagerInternal umInternal = mInjector.getUserManagerInternal();
- for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
- final int flags;
- if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
- flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
- } else if (umInternal.isUserRunning(user.id)) {
- flags = StorageManager.FLAG_STORAGE_DE;
- } else {
- continue;
- }
-
- try {
- sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
- synchronized (mInstallLock) {
- reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
- null);
- }
- } catch (IllegalStateException e) {
- // Device was probably ejected, and we'll process that event momentarily
- Slog.w(TAG, "Failed to prepare storage: " + e);
- }
- }
-
- synchronized (mLock) {
- final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
- if (isUpgrade) {
- logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint
- + " to " + Build.FINGERPRINT + "; regranting permissions for "
- + volumeUuid);
- }
- mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
-
- // Yay, everything is now upgraded
- ver.forceCurrent();
-
- writeSettingsLPrTEMP();
- }
-
- for (PackageFreezer freezer : freezers) {
- freezer.close();
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
- sendResourcesChangedBroadcast(true, false, loaded, null);
- synchronized (mLoadedVolumes) {
- mLoadedVolumes.add(vol.getId());
- }
- }
-
- private void unloadPrivatePackages(final VolumeInfo vol) {
- mHandler.post(() -> unloadPrivatePackagesInner(vol));
- }
-
- private void unloadPrivatePackagesInner(VolumeInfo vol) {
- final String volumeUuid = vol.fsUuid;
- if (TextUtils.isEmpty(volumeUuid)) {
- Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
- return;
- }
-
- final int[] userIds = mUserManager.getUserIds();
- final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
- synchronized (mInstallLock) {
- synchronized (mLock) {
- final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
- for (PackageSetting ps : packages) {
- if (ps.pkg == null) continue;
-
- final AndroidPackage pkg = ps.pkg;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
-
- try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
- "unloadPrivatePackagesInner")) {
- if (deletePackageLIF(ps.name, null, false, userIds, deleteFlags, outInfo,
- false)) {
- unloaded.add(pkg);
- } else {
- Slog.w(TAG, "Failed to unload " + ps.getPath());
- }
- }
-
- // Try very hard to release any references to this package
- // so we don't risk the system server being killed due to
- // open FDs
- AttributeCache.instance().removePackage(ps.name);
- }
-
- writeSettingsLPrTEMP();
- }
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
- sendResourcesChangedBroadcast(false, false, unloaded, null);
- synchronized (mLoadedVolumes) {
- mLoadedVolumes.remove(vol.getId());
- }
-
- // Try very hard to release any references to this path so we don't risk
- // the system server being killed due to open FDs
- ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());
-
- for (int i = 0; i < 3; i++) {
- System.gc();
- System.runFinalization();
- }
- }
-
private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
synchronized (mLock) {
@@ -20088,17 +18572,6 @@
}
}
- private List<String> collectAbsoluteCodePaths() {
- synchronized (mLock) {
- List<String> codePaths = new ArrayList<>();
- final int packageCount = mSettings.getPackagesLocked().size();
- for (int i = 0; i < packageCount; i++) {
- final PackageSetting ps = mSettings.getPackagesLocked().valueAt(i);
- codePaths.add(ps.getPath().getAbsolutePath());
- }
- return codePaths;
- }
- }
private void executeBatchLI(@NonNull Installer.Batch batch) {
try {
@@ -20109,57 +18582,6 @@
}
/**
- * Examine all apps present on given mounted volume, and destroy apps that
- * aren't expected, either due to uninstallation or reinstallation on
- * another volume.
- */
- private void reconcileApps(String volumeUuid) {
- List<String> absoluteCodePaths = collectAbsoluteCodePaths();
- List<File> filesToDelete = null;
-
- final File[] files = FileUtils.listFilesOrEmpty(
- Environment.getDataAppDirectory(volumeUuid));
- for (File file : files) {
- final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageName(file.getName());
- if (!isPackage) {
- // Ignore entries which are not packages
- continue;
- }
-
- String absolutePath = file.getAbsolutePath();
-
- boolean pathValid = false;
- final int absoluteCodePathCount = absoluteCodePaths.size();
- for (int i = 0; i < absoluteCodePathCount; i++) {
- String absoluteCodePath = absoluteCodePaths.get(i);
- if (absoluteCodePath.startsWith(absolutePath)) {
- pathValid = true;
- break;
- }
- }
-
- if (!pathValid) {
- if (filesToDelete == null) {
- filesToDelete = new ArrayList<>();
- }
- filesToDelete.add(file);
- }
- }
-
- if (filesToDelete != null) {
- final int fileToDeleteCount = filesToDelete.size();
- for (int i = 0; i < fileToDeleteCount; i++) {
- File fileToDelete = filesToDelete.get(i);
- logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete);
- synchronized (mInstallLock) {
- removeCodePathLI(fileToDelete);
- }
- }
- }
- }
-
- /**
* Reconcile all app data for the given user.
* <p>
* Verifies that directories exist and that ownership and labeling is
@@ -20186,7 +18608,7 @@
}
@GuardedBy("mInstallLock")
- private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
+ void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
boolean migrateAppData, @Nullable ArraySet<String> reconciledPackages) {
reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */,
reconciledPackages);
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
new file mode 100644
index 0000000..5fab84e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -0,0 +1,1822 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that handles package scanning logic
+ */
+public final class ScanPackageHelper {
+ final PackageManagerService mPm;
+
+ // TODO(b/198166813): remove PMS dependency
+ public ScanPackageHelper(PackageManagerService pm) {
+ mPm = pm;
+ }
+
+ /**
+ * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
+ * the app ID required for reconcile.
+ * @return {@code true} if a new app ID was registered and will need to be cleaned up on
+ * failure.
+ */
+ public boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
+ throws PackageManagerException {
+ if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
+ }
+ return false;
+ }
+
+ /**
+ * Reverts any app ID creation that were made by
+ * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
+ * referenced method returned true.
+ */
+ public void cleanUpAppIdCreation(@NonNull ScanResult result) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.mPkgSetting.appId > 0) {
+ mPm.mSettings.removeAppIdLPw(result.mPkgSetting.appId);
+ }
+ }
+
+ /**
+ * Traces a package scan.
+ * @see #scanPackageLI(File, int, int, long, UserHandle)
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
+ int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
+ try {
+ return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Similar to the other scanPackageTracedLI but accepting a ParsedPackage instead of a File.
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ try {
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * Returns {@code null} in case of errors and the error code is stored in mLastScanError
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
+ long currentTime, UserHandle user) throws PackageManagerException {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
+ parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Static shared libraries have synthetic package names
+ if (parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+ }
+
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ mPm.mPlatformPackage, mPm.mIsUpgrade, mPm.mIsPreNMR1Upgrade);
+ }
+
+ // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
+ // But, first, committing the results / removing app data needs to be moved up a level to the
+ // callers of this method. Also, we need to solve the problem of potentially creating a new
+ // shared user setting. That can probably be done later and patch things up after the fact.
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+
+ final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
+ if (realPkgName != null) {
+ ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
+ renamedPkgName);
+ final PackageSetting pkgSetting =
+ mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
+ final PackageSetting disabledPkgSetting =
+ mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
+
+ if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " was transferred to another, but its .apk remains");
+ }
+
+ scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
+ synchronized (mPm.mLock) {
+ boolean isUpdatedSystemApp;
+ if (pkgSetting != null) {
+ isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+ applyPolicy(parsedPackage, scanFlags, mPm.mPlatformPackage, isUpdatedSystemApp);
+ assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
+
+ SharedUserSetting sharedUserSetting = null;
+ if (parsedPackage.getSharedUserId() != null) {
+ // SIDE EFFECTS; may potentially allocate a new shared user
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+ }
+ String platformPackageName = mPm.mPlatformPackage == null
+ ? null : mPm.mPlatformPackage.getPackageName();
+ final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+ pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+ originalPkgSetting, realPkgName, parseFlags, scanFlags,
+ Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
+ cpuAbiOverride);
+ return scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest, currentTime);
+ }
+ }
+
+ /**
+ * Just scans the package without any side effects.
+ * <p>Not entirely true at the moment. There is still one side effect -- this
+ * method potentially modifies a live {@link PackageSetting} object representing
+ * the package being scanned. This will be resolved in the future.
+ *
+ * @param injector injector for acquiring dependencies
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mPm.mInstallLock")
+ @VisibleForTesting
+ @NonNull
+ public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ PackageManagerService.Injector injector,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+ final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
+
+ List<String> changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
+ }
+
+ // Initialize package source and resource directories
+ final File destCodeFile = new File(parsedPackage.getPath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
+ // API info from existing package setting. However, stub packages currently do not
+ // preserve ABI info, thus the special condition check here. Remove the special
+ // check after we fix the stub generation.
+ if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
+ needToDeriveAbi = true;
+ } else {
+ primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
+ secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+ }
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ boolean leavingSharedUser = false;
+
+ if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
+ if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
+ leavingSharedUser = true;
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.sharedUser.name + " to " + "<nothing>.");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.sharedUser != null
+ ? pkgSetting.sharedUser.name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ }
+
+ String[] usesStaticLibraries = null;
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
+ usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+ parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
+ }
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+ originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesStaticLibraries,
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
+ } else {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.pkg = parsedPackage;
+
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
+ UserManagerService.getInstance(),
+ usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
+ parsedPackage.getMimeGroups(), newDomainSetId);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
+ parsedPackage.setPackageName(originalPkgSetting.name);
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.realName
+ + " renamed to replace old package " + pkgSetting.name;
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+
+ final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
+ // for existing packages, change the install state; but, only if it's explicitly specified
+ if (!createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ PackageManagerService.setInstantAppForUser(
+ injector, pkgSetting, userId, instantApp, fullApp);
+ }
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
+ }
+
+ parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
+ injector.getCompatibility()));
+
+ if (parsedPackage.isSystem()) {
+ configurePackageComponents(parsedPackage);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+
+ final File appLib32InstallDir = PackageManagerService.getAppLib32InstallDir();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride, appLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ parsedPackage);
+ abis.applyTo(parsedPackage);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+ .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
+
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : "
+ + parsedPackage.getPackageName() + " "
+ + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new code path.
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
+ .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
+ appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
+ ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null) {
+ Slog.w(TAG, "Ignoring persisted ABI override for package "
+ + parsedPackage.getPackageName());
+ }
+ }
+
+ pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
+ pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.primaryCpuAbiString
+ + " secondary=" + pkgSetting.primaryCpuAbiString
+ + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
+ packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.sharedUser.packages, parsedPackage));
+ }
+
+ parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+ .contains(android.Manifest.permission.FACTORY_TEST));
+
+ if (parsedPackage.isSystem()) {
+ pkgSetting.setIsOrphaned(true);
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(parsedPackage);
+ if (currentTime != 0) {
+ if (pkgSetting.firstInstallTime == 0) {
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.lastUpdateTime = currentTime;
+ }
+ } else if (pkgSetting.firstInstallTime == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.timeStamp) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.lastUpdateTime = scanFileTime;
+ }
+ }
+ pkgSetting.setTimeStamp(scanFileTime);
+ // TODO(b/135203078): Remove, move to constructor
+ pkgSetting.pkg = parsedPackage;
+ pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
+ pkgSetting.pkgPrivateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
+ if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
+ pkgSetting.versionCode = parsedPackage.getLongVersionCode();
+ }
+ // Update volume if needed
+ final String volumeUuid = parsedPackage.getVolumeUuid();
+ if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + parsedPackage.getPackageName()
+ + " volume from " + pkgSetting.volumeUuid
+ + " to " + volumeUuid);
+ pkgSetting.volumeUuid = volumeUuid;
+ }
+
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
+ }
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+ dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+ for (String name : parsedPackage.getLibraryNames()) {
+ dynamicSharedLibraryInfos.add(
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
+ }
+ }
+
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */,
+ leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
+ }
+
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * <p>Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ * <ul>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
+ * </ul>
+ */
+ private @PackageManagerService.ScanFlags int adjustScanFlags(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
+ AndroidPackage pkg) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+ scanFlags |= SCAN_AS_PRODUCT;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
+ scanFlags |= SCAN_AS_SYSTEM_EXT;
+ }
+ if ((systemPkgSetting.pkgPrivateFlags
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtulalPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+
+ // Scan as privileged apps that share a user with a priv-app.
+ final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
+ && getVendorPartitionVersion() < 28;
+ if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
+ && !pkg.isPrivileged()
+ && (pkg.getSharedUserId() != null)
+ && !skipVendorPrivilegeScan) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+ 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ // TODO(b/72378145) Fix this exemption. Force signature apps
+ // to allowlist their privileged permissions just like other
+ // priv-apps.
+ synchronized (mPm.mLock) {
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if ((compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ pkg.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ }
+ }
+ }
+
+ return scanFlags;
+ }
+
+
+ /**
+ * Adds a new package to the internal data structures during platform initialization.
+ * <p>After adding, the package is known to the system and available for querying.
+ * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+ * etc...], additional checks are performed. Basic verification [such as ensuring
+ * matching signatures, checking version codes, etc...] occurs if the package is
+ * identical to a previously known package. If the package fails a signature check,
+ * the version installed on /data will be removed. If the version of the new package
+ * is less than or equal than the version on /data, it will be ignored.
+ * <p>Regardless of the package location, the results are applied to the internal
+ * structures and the package is made available to the rest of the system.
+ * <p>NOTE: The return value should be removed. It's the passed in package object.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, AndroidPackage platformPackage, boolean isUpgrade,
+ boolean isPreNMR1Upgrade) throws PackageManagerException {
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ final String renamedPkgName;
+ final PackageSetting disabledPkgSetting;
+ final boolean isSystemPkgUpdated;
+ final boolean pkgAlreadyExists;
+ PackageSetting pkgSetting;
+
+ synchronized (mPm.mLock) {
+ renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ final String realPkgName = getRealPackageName(parsedPackage,
+ renamedPkgName);
+ if (realPkgName != null) {
+ ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
+ renamedPkgName);
+ final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
+ parsedPackage.getPackageName());
+ pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists
+ ? pkgSetting.name : parsedPackage.getPackageName();
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app"
+ + "and install it as non-updated system app.");
+ mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+
+ final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+ ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+
+ if (scanSystemPartition) {
+ if (isSystemPkgUpdated) {
+ // we're updating the disabled package, so, scan it as the package setting
+ boolean isPlatformPackage = platformPackage != null
+ && Objects.equals(platformPackage.getPackageName(),
+ parsedPackage.getPackageName());
+ final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+ null, disabledPkgSetting /* pkgSetting */,
+ null /* disabledPkgSetting */, null /* originalPkgSetting */,
+ null, parseFlags, scanFlags, isPlatformPackage, user, null);
+ applyPolicy(parsedPackage, scanFlags,
+ platformPackage, true);
+ final ScanResult scanResult =
+ scanPackageOnlyLI(request, mPm.mInjector,
+ mPm.mFactoryTest, -1L);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+ }
+ }
+ }
+ }
+
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+ final boolean newPkgVersionGreater =
+ pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPm.mLock) {
+ // just remove the loaded entries from package lists
+ mPm.mPackages.remove(pkgSetting.name);
+ }
+
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString()
+ + " --> " + parsedPackage.getPath());
+
+ final InstallArgs args = mPm.createInstallArgsForExisting(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ args.cleanUpResourcesLI();
+ synchronized (mPm.mLock) {
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
+ }
+ }
+
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ parsedPackage.hideAsFinal();
+ throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ + " at " + parsedPackage.getPath() + " ignored: updated version "
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
+ }
+
+ // Verify certificates against what was last scanned. Force re-collecting certificate in two
+ // special cases:
+ // 1) when scanning system, force re-collect only if system is upgrading.
+ // 2) when scannning /data, force re-collect only if the app is privileged (updated from
+ // preinstall, or treated as privileged, e.g. due to shared user ID).
+ final boolean forceCollect = scanSystemPartition ? isUpgrade
+ : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ if (DEBUG_VERIFY && forceCollect) {
+ Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+ }
+
+ // Full APK verification can be skipped during certificate collection, only if the file is
+ // in verified partition, or can be verified on access (when apk verity is enabled). In both
+ // cases, only data in Signing Block is verified instead of the whole file.
+ // TODO(b/136132412): skip for Incremental installation
+ final boolean skipVerify = scanSystemPartition
+ || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify,
+ isPreNMR1Upgrade);
+
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
+ boolean shouldHideSystemApp = false;
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+
+ if (!parsedPackage.getSigningDetails()
+ .checkCapability(pkgSetting.signatures.mSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !pkgSetting.signatures.mSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.name);
+ try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+ parsedPackage.getPackageName(),
+ "scanPackageInternalLI")) {
+ mPm.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), 0, null, false);
+ }
+ pkgSetting = null;
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.name
+ + "; " + pkgSetting.versionCode + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString() + " --> "
+ + parsedPackage.getPath());
+ InstallArgs args = mPm.createInstallArgsForExisting(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ synchronized (mPm.mInstallLock) {
+ args.cleanUpResourcesLI();
+ }
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.name
+ + "; old: " + pkgSetting.getPathString() + " @ "
+ + pkgSetting.versionCode
+ + "; new: " + parsedPackage.getPath() + " @ "
+ + parsedPackage.getPath());
+ }
+ }
+
+ final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
+ | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ if (scanResult.mSuccess) {
+ synchronized (mPm.mLock) {
+ boolean appIdCreated = false;
+ try {
+ final String pkgName = scanResult.mPkgSetting.name;
+ final Map<String, ReconciledPackage> reconcileResult =
+ mPm.reconcilePackagesLocked(
+ new ReconcileRequest(
+ Collections.singletonMap(pkgName, scanResult),
+ mPm.mSharedLibraries,
+ mPm.mPackages,
+ Collections.singletonMap(
+ pkgName,
+ mPm.getSettingsVersionForPackage(
+ parsedPackage)),
+ Collections.singletonMap(pkgName,
+ mPm.getSharedLibLatestVersionSetting(
+ scanResult))),
+ mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
+ appIdCreated = optimisticallyRegisterAppId(scanResult);
+ mPm.commitReconciledScanResultLocked(
+ reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+ } catch (PackageManagerException e) {
+ if (appIdCreated) {
+ cleanUpAppIdCreation(scanResult);
+ }
+ throw e;
+ }
+ }
+ }
+
+ if (shouldHideSystemApp) {
+ synchronized (mPm.mLock) {
+ mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
+ }
+ }
+ if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (pkgSetting != null && pkgSetting.isPackageLoading()) {
+ // Continue monitoring loading progress of active incremental packages
+ final IncrementalStatesCallback incrementalStatesCallback =
+ new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
+ pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
+ mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
+ }
+ }
+ return scanResult.mPkgSetting.pkg;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped for the whole package, including splits.
+ */
+ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+ if (!canSkipForcedApkVerification(pkg.getBaseApkPath())) {
+ return false;
+ }
+ // TODO: Allow base and splits to be verified individually.
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if (!canSkipForcedApkVerification(splitCodePaths[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+ * whether the apk contains signed root hash. Note that the signer's certificate still needs to
+ * match one in a trusted source, and should be done separately.
+ */
+ private boolean canSkipForcedApkVerification(String apkPath) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+ return VerityUtils.hasFsverity(apkPath);
+ }
+
+ try {
+ final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+ if (rootHashObserved == null) {
+ return false; // APK does not contain Merkle tree root hash.
+ }
+ synchronized (mPm.mInstallLock) {
+ // Returns whether the observed root hash matches what kernel has.
+ mPm.mInstaller.assertFsverityRootHashMatches(apkPath, rootHashObserved);
+ return true;
+ }
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+ }
+ return false;
+ }
+
+ private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+ boolean forceCollect, boolean skipVerify, boolean mIsPreNMR1Upgrade)
+ throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = mIsPreNMR1Upgrade
+ ? new File(parsedPackage.getPath()).lastModified()
+ : getLastModifiedTime(parsedPackage);
+ final Settings.VersionInfo settingsVersionForPackage =
+ mPm.getSettingsVersionForPackage(parsedPackage);
+ if (ps != null && !forceCollect
+ && ps.getPathString().equals(parsedPackage.getPath())
+ && ps.timeStamp == lastModifiedTime
+ && !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !PackageManagerService.isRecoverSignatureUpdateNeeded(
+ settingsVersionForPackage)) {
+ if (ps.signatures.mSigningDetails.getSignatures() != null
+ && ps.signatures.mSigningDetails.getSignatures().length != 0
+ && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
+ != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
+ // if the package appears to be unchanged.
+ parsedPackage.setSigningDetails(
+ new SigningDetails(ps.signatures.mSigningDetails));
+ return;
+ }
+
+ Slog.w(TAG, "PackageSetting for " + ps.name
+ + " is missing signatures. Collecting certs again to recover them.");
+ } else {
+ Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ + (forceCollect ? " (forced)" : ""));
+ }
+
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull AndroidPackage pkg) {
+ if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+ return;
+ }
+ if (originalPkgSetting.versionCode == pkg.getLongVersionCode()) {
+ return;
+ }
+
+ mPm.clearAppProfilesLIF(pkg);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.name
+ + " clear profile due to version change "
+ + originalPkgSetting.versionCode + " != "
+ + pkg.getLongVersionCode());
+ }
+ }
+
+ /**
+ * Returns the original package setting.
+ * <p>A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ * <p>An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable
+ private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return null;
+ }
+ for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.sharedUser != null) {
+ if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ + " to " + pkg.getPackageName() + ": old uid "
+ + originalPs.sharedUser.name
+ + " differs from " + pkg.getSharedUserId());
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) {
+ Log.v(TAG, "Renaming new package "
+ + pkg.getPackageName() + " to old name " + originalPs.name);
+ }
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mPm.mLock")
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
+ if ((oldPkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.getPackageName()
+ + ": old package not in system partition");
+ return false;
+ } else if (mPm.mPackages.get(oldPkg.name) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.getPackageName()
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Asserts the parsed package is valid according to the given policy. If the
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
+ * <p>
+ * Implementation detail: This method must NOT have any side effects. It would
+ * ideally be static, but, it requires locks to read system state.
+ *
+ * @throws PackageManagerException If the package fails any of the validation checks
+ */
+ private void assertPackageIsValid(AndroidPackage pkg,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ final @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
+ assertCodePolicy(pkg);
+ }
+
+ if (pkg.getPath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Check that there is an APEX package with the same name only during install/first boot
+ // after OTA.
+ final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
+ final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if ((isUserInstall || isFirstBootOrUpgrade)
+ && mPm.mApexManager.isApexPackage(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ pkg.getPackageName()
+ + " is an APEX package and can't be installed as an APK.");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ ksms.assertScannedPackageValid(pkg);
+
+ synchronized (mPm.mLock) {
+ // The special "android" package can only be defined once
+ if (pkg.getPackageName().equals("android")) {
+ if (mPm.mAndroidApplication != null) {
+ Slog.w(TAG, "*************************************************");
+ Slog.w(TAG, "Core android package being redefined. Skipping.");
+ Slog.w(TAG, " codePath=" + pkg.getPath());
+ Slog.w(TAG, "*************************************************");
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Core android package being redefined. Skipping.");
+ }
+ }
+
+ // A package name must be unique; don't allow duplicates
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.getPackageName()
+ + " already installed. Skipping duplicate.");
+ }
+
+ if (pkg.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // but we still want the base name to be unique.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
+ throw new PackageManagerException(
+ "Duplicate static shared lib provider package");
+ }
+
+ // Static shared libraries should have at least O target SDK
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs must target O SDK or higher");
+ }
+
+ // Package declaring static a shared lib cannot be instant apps
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be instant apps");
+ }
+
+ // Package declaring static a shared lib cannot be renamed since the package
+ // name is synthetic and apps can't code around package manager internals.
+ if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be renamed");
+ }
+
+ // Package declaring static a shared lib cannot declare dynamic libs
+ if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare dynamic libs");
+ }
+
+ // Package declaring static a shared lib cannot declare shared users
+ if (pkg.getSharedUserId() != null) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare shared users");
+ }
+
+ // Static shared libs cannot declare activities
+ if (!pkg.getActivities().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare activities");
+ }
+
+ // Static shared libs cannot declare services
+ if (!pkg.getServices().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare services");
+ }
+
+ // Static shared libs cannot declare providers
+ if (!pkg.getProviders().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare content providers");
+ }
+
+ // Static shared libs cannot declare receivers
+ if (!pkg.getReceivers().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare broadcast receivers");
+ }
+
+ // Static shared libs cannot declare permission groups
+ if (!pkg.getPermissionGroups().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permission groups");
+ }
+
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare features");
+ }
+
+ // Static shared libs cannot declare permissions
+ if (!pkg.getPermissions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permissions");
+ }
+
+ // Static shared libs cannot declare protected broadcasts
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare protected broadcasts");
+ }
+
+ // Static shared libs cannot be overlay targets
+ if (pkg.getOverlayTarget() != null) {
+ throw new PackageManagerException(
+ "Static shared libs cannot be overlay targets");
+ }
+
+ // The version codes must be ordered as lib versions
+ long minVersionCode = Long.MIN_VALUE;
+ long maxVersionCode = Long.MAX_VALUE;
+
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(i);
+ final long libVersionCode = libInfo.getDeclaringPackage()
+ .getLongVersionCode();
+ if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getLongVersion()
+ > pkg.getStaticSharedLibVersion()) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.getLongVersionCode() < minVersionCode
+ || pkg.getLongVersionCode() > maxVersionCode) {
+ throw new PackageManagerException("Static shared"
+ + " lib version codes must be ordered as lib versions");
+ }
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mPm.mExpectingBetter.containsKey(pkg.getPackageName())) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
+ } else {
+ PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.getPath()
+ + " and requiring known path " + known.getPathString());
+ }
+ if (!pkg.getPath().equals(known.getPathString())) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.getPackageName()
+ + " found at " + pkg.getPath()
+ + " but expected at " + known.getPathString()
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.getPackageName()
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ mPm.mComponentResolver.assertProvidersNotDefined(pkg);
+ }
+
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+ 0, 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Apps that share a user with a "
+ + "privileged app must themselves be marked as privileged. "
+ + pkg.getPackageName() + " shares privileged user "
+ + pkg.getSharedUserId() + ".");
+ }
+ }
+ }
+
+ // Apply policies specific for runtime resource overlays (RROs).
+ if (pkg.getOverlayTarget() != null) {
+ // System overlays have some restrictions on their use of the 'static' state.
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ // We are scanning a system overlay. This can be the first scan of the
+ // system/vendor/oem partition, or an update to the system overlay.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ Objects.requireNonNull(mPm.mOverlayConfig,
+ "Parsing non-system dir before overlay configs are initialized");
+ if (!mPm.mOverlayConfig.isMutable(pkg.getPackageName())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " is static and cannot be upgraded.");
+ }
+ } else {
+ if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of vendor"
+ + " overlays (" + getVendorPartitionVersion() + ")."
+ + " This will become an install error in a future release");
+ }
+ } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of system"
+ + " overlays (" + Build.VERSION.SDK_INT + ")."
+ + " This will become an install error in a future release");
+ }
+ }
+ } else {
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without <overlay android:targetName>, will
+ // only be used if it is signed with the same certificate as its target OR if
+ // it is signed with the same certificate as a reference package declared
+ // in 'overlay-config-signature' tag of SystemConfig.
+ // If the target is already installed or 'overlay-config-signature' tag in
+ // SystemConfig is set, check this here to augment the last line of defense
+ // which is OMS.
+ if (pkg.getOverlayTargetOverlayableName() == null) {
+ final PackageSetting targetPkgSetting =
+ mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
+ if (targetPkgSetting != null) {
+ if (!comparePackageSignatures(targetPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ // check reference signature
+ if (mPm.mOverlayConfigSignaturePackage == null) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " and target "
+ + pkg.getOverlayTarget() + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " <overlay android:targetName>");
+ }
+ final PackageSetting refPkgSetting =
+ mPm.mSettings.getPackageLPr(
+ mPm.mOverlayConfigSignaturePackage);
+ if (!comparePackageSignatures(refPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " signed with a different "
+ + "certificate than both the reference package and "
+ + "target " + pkg.getOverlayTarget() + ", and the "
+ + "overlay lacks <overlay android:targetName>");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If the package is not on a system partition ensure it is signed with at least the
+ // minimum signature scheme version required for its target SDK.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
+ }
+ }
+ }
+ }
+
+ private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
+ List<T> components, Map<String, ParsedProcess> procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ParsedMainComponent component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.getClassName()
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ }
+ }
+
+ /**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
+ * relevant packages and settings.
+ * @param sharedUserSetting The {@code SharedUserSetting} to adjust
+ * @param scannedPackage the package being scanned or null
+ * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
+ * @return the list of code paths that belong to packages that had their ABIs adjusted.
+ */
+ public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
+ ParsedPackage scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.setPrimaryCpuAbi(adjustedAbi);
+ }
+ List<String> changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
+ if (ps.primaryCpuAbiString != null) {
+ continue;
+ }
+
+ ps.primaryCpuAbiString = adjustedAbi;
+ if (ps.pkg != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.getPathString());
+ }
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+ /**
+ * Applies policy to the parsed package based upon the given policy flags.
+ * Ensures the package is in a good state.
+ * <p>
+ * Implementation detail: This method must NOT have any side effect. It would
+ * ideally be static, but, it requires locks to read system state.
+ */
+ private static void applyPolicy(ParsedPackage parsedPackage,
+ final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ parsedPackage.setSystem(true);
+ // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+ // is set during parse.
+ if (parsedPackage.isDirectBootAware()) {
+ parsedPackage.setAllComponentsDirectBootAware(true);
+ }
+ if (compressedFileExists(parsedPackage.getPath())) {
+ parsedPackage.setStub(true);
+ }
+ } else {
+ parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
+ // non system apps can't be flagged as core
+ .setCoreApp(false)
+ // clear flags not applicable to regular apps
+ .setPersistent(false)
+ .setDefaultToDeviceProtectedStorage(false)
+ .setDirectBootAware(false)
+ // non system apps can't have permission priority
+ .capPermissionPriorities();
+ }
+ if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ parsedPackage
+ .markNotActivitiesAsNotExportedIfSingleUser();
+ }
+
+ parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+ .setOem((scanFlags & SCAN_AS_OEM) != 0)
+ .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+ .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+ .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+ .setOdm((scanFlags & SCAN_AS_ODM) != 0);
+
+ // Check if the package is signed with the same key as the platform package.
+ parsedPackage.setSignedWithPlatformKey(
+ (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+ || (platformPkg != null && compareSignatures(
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
+ ) == PackageManager.SIGNATURE_MATCH))
+ );
+
+ if (!parsedPackage.isSystem()) {
+ // Only system apps can use these features.
+ parsedPackage.clearOriginalPackages()
+ .clearAdoptPermissions();
+ }
+
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ private static void assertCodePolicy(AndroidPackage pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode = pkg.isHasCode();
+ if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getBaseApkPath() + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+ for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ * <p>This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return AndroidPackageUtils.getRealPackageOrNull(pkg);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.getOriginalPackages().contains(renamedPkgName);
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ * <p>When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ @NonNull String renamedPackageName) {
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ || parsedPackage.getPackageName().equals(renamedPackageName)) {
+ return;
+ }
+ parsedPackage.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ private static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
+ **/
+ private static void configurePackageComponents(AndroidPackage pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.getPackageName());
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getActivities().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getReceivers().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
+ final ParsedProvider component = pkg.getProviders().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
+ final ParsedService component = pkg.getServices().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ component.setEnabled(enabled);
+ }
+ }
+ }
+
+ private static int getVendorPartitionVersion() {
+ final String version = SystemProperties.get("ro.vndk.version");
+ if (!version.isEmpty()) {
+ try {
+ return Integer.parseInt(version);
+ } catch (NumberFormatException ignore) {
+ if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ }
+ }
+ return Build.VERSION_CODES.P;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 95e2d0a..2c649d2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -20,7 +20,7 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE;
@@ -1040,14 +1040,15 @@
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
final String pkgName = pkgSetting.name;
- if (pkgSetting.sharedUser != sharedUser) {
+ if (!Objects.equals(pkgSetting.sharedUser, sharedUser) && sharedUser != null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + pkgName + " shared user changed from "
+ (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
- + " to " + (sharedUser != null ? sharedUser.name : "<nothing>"));
- throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ + " to " + sharedUser.name);
+ throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
"Updating application package " + pkgName + " failed");
}
+ pkgSetting.sharedUser = sharedUser;
if (!pkgSetting.getPath().equals(codePath)) {
final boolean isSystem = pkgSetting.isSystem();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 0951e0d..4eb3b54 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3119,8 +3119,6 @@
// step. Remove them too.
user.forAllPackages(p -> p.refreshPinnedFlags());
- scheduleSaveUser(owningUserId);
-
if (doNotify) {
notifyListeners(packageName, owningUserId);
}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
new file mode 100644
index 0000000..e8dcfc7
--- /dev/null
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -0,0 +1,344 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+
+import android.app.ResourcesManager;
+import android.content.IIntentReceiver;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.Build;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.UserHandle;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.policy.AttributeCache;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Helper class to handle storage events and private apps loading */
+public class StorageEventHelper extends StorageEventListener {
+ final PackageManagerService mPm;
+
+ // TODO(b/198166813): remove PMS dependency
+ public StorageEventHelper(PackageManagerService pm) {
+ mPm = pm;
+ }
+
+ @Override
+ public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+ if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+ if (vol.state == VolumeInfo.STATE_MOUNTED) {
+ final String volumeUuid = vol.getFsUuid();
+
+ // Clean up any users or apps that were removed or recreated
+ // while this volume was missing
+ mPm.mUserManager.reconcileUsers(volumeUuid);
+ reconcileApps(volumeUuid);
+
+ // Clean up any install sessions that expired or were
+ // cancelled while this volume was missing
+ mPm.mInstallerService.onPrivateVolumeMounted(volumeUuid);
+
+ loadPrivatePackages(vol);
+
+ } else if (vol.state == VolumeInfo.STATE_EJECTING) {
+ unloadPrivatePackages(vol);
+ }
+ }
+ }
+
+ @Override
+ public void onVolumeForgotten(String fsUuid) {
+ if (TextUtils.isEmpty(fsUuid)) {
+ Slog.e(TAG, "Forgetting internal storage is probably a mistake; ignoring");
+ return;
+ }
+
+ // Remove any apps installed on the forgotten volume
+ synchronized (mPm.mLock) {
+ final List<PackageSetting> packages = mPm.mSettings.getVolumePackagesLPr(fsUuid);
+ for (PackageSetting ps : packages) {
+ Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
+ mPm.deletePackageVersioned(new VersionedPackage(ps.name,
+ PackageManager.VERSION_CODE_HIGHEST),
+ new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
+ UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
+ // Try very hard to release any references to this package
+ // so we don't risk the system server being killed due to
+ // open FDs
+ AttributeCache.instance().removePackage(ps.name);
+ }
+
+ mPm.mSettings.onVolumeForgotten(fsUuid);
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+
+ private void loadPrivatePackages(final VolumeInfo vol) {
+ mPm.mHandler.post(() -> loadPrivatePackagesInner(vol));
+ }
+
+ private void loadPrivatePackagesInner(VolumeInfo vol) {
+ final String volumeUuid = vol.fsUuid;
+ if (TextUtils.isEmpty(volumeUuid)) {
+ Slog.e(TAG, "Loading internal storage is probably a mistake; ignoring");
+ return;
+ }
+
+ final ArrayList<PackageFreezer> freezers = new ArrayList<>();
+ final ArrayList<AndroidPackage> loaded = new ArrayList<>();
+ final int parseFlags = mPm.mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
+
+ final Settings.VersionInfo ver;
+ final List<PackageSetting> packages;
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+ synchronized (mPm.mLock) {
+ ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
+ packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
+ }
+
+ for (PackageSetting ps : packages) {
+ freezers.add(mPm.freezePackage(ps.name, "loadPrivatePackagesInner"));
+ synchronized (mPm.mInstallLock) {
+ final AndroidPackage pkg;
+ try {
+ pkg = scanPackageHelper.scanPackageTracedLI(
+ ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
+ loaded.add(pkg);
+
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage());
+ }
+
+ if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
+ mPm.clearAppDataLIF(
+ ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
+ | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
+ }
+ }
+ }
+
+ // Reconcile app data for all started/unlocked users
+ final StorageManager sm = mPm.mInjector.getSystemService(StorageManager.class);
+ UserManagerInternal umInternal = mPm.mInjector.getUserManagerInternal();
+ for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
+ final int flags;
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+ } else if (umInternal.isUserRunning(user.id)) {
+ flags = StorageManager.FLAG_STORAGE_DE;
+ } else {
+ continue;
+ }
+
+ try {
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
+ synchronized (mPm.mInstallLock) {
+ mPm.reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
+ null);
+ }
+ } catch (IllegalStateException e) {
+ // Device was probably ejected, and we'll process that event momentarily
+ Slog.w(TAG, "Failed to prepare storage: " + e);
+ }
+ }
+
+ synchronized (mPm.mLock) {
+ final boolean isUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
+ if (isUpgrade) {
+ logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint
+ + " to " + Build.FINGERPRINT + "; regranting permissions for "
+ + volumeUuid);
+ }
+ mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade);
+
+ // Yay, everything is now upgraded
+ ver.forceCurrent();
+
+ mPm.writeSettingsLPrTEMP();
+ }
+
+ for (PackageFreezer freezer : freezers) {
+ freezer.close();
+ }
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
+ sendResourcesChangedBroadcast(true, false, loaded, null);
+ synchronized (mPm.mLoadedVolumes) {
+ mPm.mLoadedVolumes.add(vol.getId());
+ }
+ }
+
+ private void unloadPrivatePackages(final VolumeInfo vol) {
+ mPm.mHandler.post(() -> unloadPrivatePackagesInner(vol));
+ }
+
+ private void unloadPrivatePackagesInner(VolumeInfo vol) {
+ final String volumeUuid = vol.fsUuid;
+ if (TextUtils.isEmpty(volumeUuid)) {
+ Slog.e(TAG, "Unloading internal storage is probably a mistake; ignoring");
+ return;
+ }
+
+ final int[] userIds = mPm.mUserManager.getUserIds();
+ final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
+ synchronized (mPm.mInstallLock) {
+ synchronized (mPm.mLock) {
+ final List<PackageSetting> packages =
+ mPm.mSettings.getVolumePackagesLPr(volumeUuid);
+ for (PackageSetting ps : packages) {
+ if (ps.pkg == null) continue;
+
+ final AndroidPackage pkg = ps.pkg;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
+
+ try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.name, deleteFlags,
+ "unloadPrivatePackagesInner")) {
+ if (mPm.deletePackageLIF(ps.name, null, false, userIds, deleteFlags,
+ outInfo, false)) {
+ unloaded.add(pkg);
+ } else {
+ Slog.w(TAG, "Failed to unload " + ps.getPath());
+ }
+ }
+
+ // Try very hard to release any references to this package
+ // so we don't risk the system server being killed due to
+ // open FDs
+ AttributeCache.instance().removePackage(ps.name);
+ }
+
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
+ sendResourcesChangedBroadcast(false, false, unloaded, null);
+ synchronized (mPm.mLoadedVolumes) {
+ mPm.mLoadedVolumes.remove(vol.getId());
+ }
+
+ // Try very hard to release any references to this path so we don't risk
+ // the system server being killed due to open FDs
+ ResourcesManager.getInstance().invalidatePath(vol.getPath().getAbsolutePath());
+
+ for (int i = 0; i < 3; i++) {
+ System.gc();
+ System.runFinalization();
+ }
+ }
+
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
+ final int size = packages.size();
+ final String[] packageNames = new String[size];
+ final int[] packageUids = new int[size];
+ for (int i = 0; i < size; i++) {
+ final AndroidPackage pkg = packages.get(i);
+ packageNames[i] = pkg.getPackageName();
+ packageUids[i] = pkg.getUid();
+ }
+ mPm.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
+ finishedReceiver);
+ }
+
+ /**
+ * Examine all apps present on given mounted volume, and destroy apps that
+ * aren't expected, either due to uninstallation or reinstallation on
+ * another volume.
+ */
+ public void reconcileApps(String volumeUuid) {
+ List<String> absoluteCodePaths = collectAbsoluteCodePaths();
+ List<File> filesToDelete = null;
+
+ final File[] files = FileUtils.listFilesOrEmpty(
+ Environment.getDataAppDirectory(volumeUuid));
+ for (File file : files) {
+ final boolean isPackage = (isApkFile(file) || file.isDirectory())
+ && !PackageInstallerService.isStageName(file.getName());
+ if (!isPackage) {
+ // Ignore entries which are not packages
+ continue;
+ }
+
+ String absolutePath = file.getAbsolutePath();
+
+ boolean pathValid = false;
+ final int absoluteCodePathCount = absoluteCodePaths.size();
+ for (int i = 0; i < absoluteCodePathCount; i++) {
+ String absoluteCodePath = absoluteCodePaths.get(i);
+ if (absoluteCodePath.startsWith(absolutePath)) {
+ pathValid = true;
+ break;
+ }
+ }
+
+ if (!pathValid) {
+ if (filesToDelete == null) {
+ filesToDelete = new ArrayList<>();
+ }
+ filesToDelete.add(file);
+ }
+ }
+
+ if (filesToDelete != null) {
+ final int fileToDeleteCount = filesToDelete.size();
+ for (int i = 0; i < fileToDeleteCount; i++) {
+ File fileToDelete = filesToDelete.get(i);
+ logCriticalInfo(Log.WARN, "Destroying orphaned at " + fileToDelete);
+ synchronized (mPm.mInstallLock) {
+ mPm.removeCodePathLI(fileToDelete);
+ }
+ }
+ }
+ }
+
+ private List<String> collectAbsoluteCodePaths() {
+ synchronized (mPm.mLock) {
+ List<String> codePaths = new ArrayList<>();
+ final int packageCount = mPm.mSettings.getPackagesLocked().size();
+ for (int i = 0; i < packageCount; i++) {
+ final PackageSetting ps = mPm.mSettings.getPackagesLocked().valueAt(i);
+ codePaths.add(ps.getPath().getAbsolutePath());
+ }
+ return codePaths;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 95c9191..c8deffb 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -1,14 +1,6 @@
{
"presubmit": [
{
- "name": "CtsAtomicInstallTestCases",
- "file_patterns": [
- "core/java/.*Install.*",
- "services/core/.*Install.*",
- "services/core/java/com/android/server/pm/.*"
- ]
- },
- {
"name": "CtsPackageInstallTestCases",
"file_patterns": [
"core/java/.*Install.*",
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index de1e25a..e40cb40 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -6131,9 +6131,23 @@
proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
} else {
- startedOpResult = appOpsManager.startProxyOpNoThrow(startedOp,
- resolvedAttributionSource, message, skipProxyOperation,
- proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
+ try {
+ startedOpResult = appOpsManager.startProxyOpNoThrow(startedOp,
+ resolvedAttributionSource, message, skipProxyOperation,
+ proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
+ } catch (SecurityException e) {
+ //TODO 195339480: remove
+ String msg = "Security exception for op " + startedOp + " with source "
+ + attributionSource.getUid() + ":"
+ + attributionSource.getPackageName() + ", "
+ + attributionSource.getNextUid() + ":"
+ + attributionSource.getNextPackageName();
+ if (attributionSource.getNext() != null) {
+ AttributionSource next = attributionSource.getNext();
+ msg = msg + ", " + next.getNextPackageName() + ":" + next.getNextUid();
+ }
+ throw new SecurityException(msg + ":" + e.getMessage());
+ }
}
return Math.max(checkedOpResult, startedOpResult);
} else {
@@ -6180,8 +6194,22 @@
message, skipProxyOperation);
}
} else {
- notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp,
- resolvedAttributionSource, message, skipProxyOperation);
+ try {
+ notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp,
+ resolvedAttributionSource, message, skipProxyOperation);
+ } catch (SecurityException e) {
+ //TODO 195339480: remove
+ String msg = "Security exception for op " + notedOp + " with source "
+ + attributionSource.getUid() + ":"
+ + attributionSource.getPackageName() + ", "
+ + attributionSource.getNextUid() + ":"
+ + attributionSource.getNextPackageName();
+ if (attributionSource.getNext() != null) {
+ AttributionSource next = attributionSource.getNext();
+ msg = msg + ", " + next.getNextPackageName() + ":" + next.getNextUid();
+ }
+ throw new SecurityException(msg + ":" + e.getMessage());
+ }
}
return Math.max(checkedOpResult, notedOpResult);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ccd9709..3185a28 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -230,6 +230,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.List;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
@@ -3385,13 +3386,18 @@
}
}
}
- } else if (ExtconUEventObserver.extconExists()
- && ExtconUEventObserver.namedExtconDirExists(HdmiVideoExtconUEventObserver.NAME)) {
- HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
- plugged = observer.init();
- mHDMIObserver = observer;
- } else if (localLOGV) {
- Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
+ } else {
+ final List<ExtconUEventObserver.ExtconInfo> extcons =
+ ExtconUEventObserver.ExtconInfo.getExtconInfoForTypes(
+ new String[] {ExtconUEventObserver.ExtconInfo.EXTCON_HDMI});
+ if (!extcons.isEmpty()) {
+ // TODO: handle more than one HDMI
+ HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
+ plugged = observer.init(extcons.get(0));
+ mHDMIObserver = observer;
+ } else if (localLOGV) {
+ Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
+ }
}
// This dance forces the code in setHdmiPlugged to run.
@@ -5604,23 +5610,23 @@
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
private static final String NAME = "hdmi";
- private final ExtconInfo mHdmi = new ExtconInfo(NAME);
- private boolean init() {
+ private boolean init(ExtconInfo hdmi) {
boolean plugged = false;
try {
- plugged = parseStateFromFile(mHdmi);
+ plugged = parseStateFromFile(hdmi);
} catch (FileNotFoundException e) {
- Slog.w(TAG, mHdmi.getStatePath()
- + " not found while attempting to determine initial state", e);
+ Slog.w(TAG,
+ hdmi.getStatePath()
+ + " not found while attempting to determine initial state",
+ e);
} catch (IOException e) {
- Slog.e(
- TAG,
- "Error reading " + mHdmi.getStatePath()
+ Slog.e(TAG,
+ "Error reading " + hdmi.getStatePath()
+ " while attempting to determine initial state",
e);
}
- startObserving(mHdmi);
+ startObserving(hdmi);
return plugged;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9e61dce..9943b99 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -31,6 +31,8 @@
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
import static android.os.PowerManagerInternal.wakefulnessToString;
+import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -104,6 +106,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
@@ -1854,6 +1857,9 @@
+ ", details=" + details
+ ")...");
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
+ // The instrument will be timed out automatically after 2 seconds.
+ LatencyTracker.getInstance(mContext)
+ .onActionStart(ACTION_TURN_ON_SCREEN, String.valueOf(groupId));
setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
opPackageName, details);
@@ -3237,6 +3243,7 @@
&& mDisplayGroupPowerStateMapper.getWakefulnessLocked(
groupId) == WAKEFULNESS_AWAKE) {
mDisplayGroupPowerStateMapper.setPoweringOnLocked(groupId, false);
+ LatencyTracker.getInstance(mContext).onActionEnd(ACTION_TURN_ON_SCREEN);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
final int latencyMs = (int) (mClock.uptimeMillis()
- mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId));
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 1ebb722..92e0845 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1266,12 +1266,14 @@
if (inputId != null) {
if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
if (previousCableConnectionStatus != connection.getInputStateLocked()) {
- mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
} else {
if ((previousConfigsLength == 0)
!= (connection.getConfigsLengthLocked() == 0)) {
- mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6d89daa..6222808 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3062,12 +3062,35 @@
}
}
+ private boolean packageBelongsToUid(String packageName, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ int packageUid;
+ try {
+ packageUid = mContext.getPackageManager().getPackageUidAsUser(
+ packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return packageUid == uid;
+ }
+
+ private void enforcePackageBelongsToUid(String packageName, int uid) {
+ if (!packageBelongsToUid(packageName, uid)) {
+ throw new IllegalArgumentException(
+ "Invalid package or package does not belong to uid:"
+ + uid);
+ }
+ }
+
/**
* Certain user types do not support wallpapers (e.g. managed profiles). The check is
* implemented through through the OP_WRITE_WALLPAPER AppOp.
*/
public boolean isWallpaperSupported(String callingPackage) {
- return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, Binder.getCallingUid(),
+ final int callingUid = Binder.getCallingUid();
+ enforcePackageBelongsToUid(callingPackage, callingUid);
+
+ return mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_WRITE_WALLPAPER, callingUid,
callingPackage) == AppOpsManager.MODE_ALLOWED;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2b57179..5441414 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1590,7 +1590,7 @@
boolean focusedWindowAdded = false;
final int visibleWindowCount = visibleWindows.size();
- HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>();
ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
@@ -1610,12 +1610,14 @@
final Region regionInScreen = new Region();
computeWindowRegionInScreen(windowState, regionInScreen);
-
- if (windowMattersToAccessibility(windowState, regionInScreen, unaccountedSpace,
- skipRemainingWindowsForTasks)) {
+ if (windowMattersToAccessibility(windowState,
+ regionInScreen, unaccountedSpace,
+ skipRemainingWindowsForTaskFragments)) {
addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
- updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
- skipRemainingWindowsForTasks);
+ if (windowMattersToUnaccountedSpaceComputation(windowState)) {
+ updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
+ skipRemainingWindowsForTaskFragments);
+ }
focusedWindowAdded |= windowState.isFocused();
} else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
// If this widow is navigation bar without touchable region, accounting the
@@ -1664,9 +1666,28 @@
mInitialized = true;
}
+ // Some windows should be excluded from unaccounted space computation, though they still
+ // should be reported
+ private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) {
+ // Do not account space of trusted non-touchable windows, except the split-screen
+ // divider.
+ // If it's not trusted, touch events are not sent to the windows behind it.
+ if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+ && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)
+ && windowState.isTrustedOverlay()) {
+ return false;
+ }
+
+ if (windowState.mAttrs.type
+ == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+ return false;
+ }
+ return true;
+ }
+
private boolean windowMattersToAccessibility(WindowState windowState,
Region regionInScreen, Region unaccountedSpace,
- HashSet<Integer> skipRemainingWindowsForTasks) {
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
final RecentsAnimationController controller = mService.getRecentsAnimationController();
if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
return false;
@@ -1677,8 +1698,9 @@
}
// If the window is part of a task that we're finished with - ignore.
- final Task task = windowState.getTask();
- if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null
+ && skipRemainingWindowsForTaskFragments.contains(taskFragment)) {
return false;
}
@@ -1704,54 +1726,51 @@
}
private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
- Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
- if (windowState.mAttrs.type
- != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+ Region unaccountedSpace,
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
+ // Account for the space this window takes if the window
+ // is not an accessibility overlay which does not change
+ // the reported windows.
+ unaccountedSpace.op(regionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
- // Account for the space this window takes if the window
- // is not an accessibility overlay which does not change
- // the reported windows.
- unaccountedSpace.op(regionInScreen, unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
-
- // If a window is modal it prevents other windows from being touched
- if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
- if (!windowState.hasTapExcludeRegion()) {
- // Account for all space in the task, whether the windows in it are
- // touchable or not. The modal window blocks all touches from the task's
- // area.
- unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- } else {
- // If a window has tap exclude region, we need to account it.
- final Region displayRegion = new Region(windowState.getDisplayFrame());
- final Region tapExcludeRegion = new Region();
- windowState.getTapExcludeRegion(tapExcludeRegion);
- displayRegion.op(tapExcludeRegion, displayRegion,
- Region.Op.REVERSE_DIFFERENCE);
- unaccountedSpace.op(displayRegion, unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
- }
-
- final Task task = windowState.getTask();
- if (task != null) {
- // If the window is associated with a particular task, we can skip the
- // rest of the windows for that task.
- skipRemainingWindowsForTasks.add(task.mTaskId);
- } else if (!windowState.hasTapExcludeRegion()) {
- // If the window is not associated with a particular task, then it is
- // globally modal. In this case we can skip all remaining windows when
- // it doesn't has tap exclude region.
- unaccountedSpace.setEmpty();
- }
- }
-
- // Account for the space of letterbox.
- if (windowState.areAppWindowBoundsLetterboxed()) {
- unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace,
+ // If a window is modal it prevents other windows from being touched
+ if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ if (!windowState.hasTapExcludeRegion()) {
+ // Account for all space in the task, whether the windows in it are
+ // touchable or not. The modal window blocks all touches from the task's
+ // area.
+ unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ } else {
+ // If a window has tap exclude region, we need to account it.
+ final Region displayRegion = new Region(windowState.getDisplayFrame());
+ final Region tapExcludeRegion = new Region();
+ windowState.getTapExcludeRegion(tapExcludeRegion);
+ displayRegion.op(tapExcludeRegion, displayRegion,
+ Region.Op.REVERSE_DIFFERENCE);
+ unaccountedSpace.op(displayRegion, unaccountedSpace,
Region.Op.REVERSE_DIFFERENCE);
}
+
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null) {
+ // If the window is associated with a particular task, we can skip the
+ // rest of the windows for that task.
+ skipRemainingWindowsForTaskFragments.add(taskFragment);
+ } else if (!windowState.hasTapExcludeRegion()) {
+ // If the window is not associated with a particular task, then it is
+ // globally modal. In this case we can skip all remaining windows when
+ // it doesn't has tap exclude region.
+ unaccountedSpace.setEmpty();
+ }
+ }
+
+ // Account for the space of letterbox.
+ if (windowState.areAppWindowBoundsLetterboxed()) {
+ unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 042f4f6..946fa44 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2380,6 +2380,17 @@
});
}
+ void removeStartingWindowIfNeeded() {
+ // Removing the task snapshot after the task is actually focused (see
+ // Task#onWindowFocusChanged). Since some of the app contents may draw in this time and
+ // requires more times to draw finish, in case flicking may happen when removing the task
+ // snapshot too early. (i.e. Showing IME.)
+ if ((mStartingData instanceof SnapshotStartingData) && !getTask().isFocused()) {
+ return;
+ }
+ removeStartingWindow();
+ }
+
void removeStartingWindow() {
if (transferSplashScreenIfNeeded()) {
return;
@@ -2596,6 +2607,11 @@
return parent != null ? parent.getOrganizedTaskFragment() : null;
}
+ boolean isEmbedded() {
+ final TaskFragment parent = getTaskFragment();
+ return parent != null && parent.isEmbedded();
+ }
+
@Override
@Nullable
TaskDisplayArea getDisplayArea() {
@@ -2687,7 +2703,9 @@
boolean isResizeable() {
return mAtmService.mForceResizableActivities
|| ActivityInfo.isResizeableMode(info.resizeMode)
- || info.supportsPictureInPicture();
+ || info.supportsPictureInPicture()
+ // If the activity can be embedded, it should inherit the bounds of task fragment.
+ || isEmbedded();
}
/** @return whether this activity is non-resizeable but is forced to be resizable. */
@@ -6030,13 +6048,13 @@
final Task associatedTask =
mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
if (associatedTask == null) {
- removeStartingWindow();
+ removeStartingWindowIfNeeded();
} else if (associatedTask.getActivity(
r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
// The last drawn activity may not be the one that owns the starting window.
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
if (r != null) {
- r.removeStartingWindow();
+ r.removeStartingWindowIfNeeded();
}
}
updateReportedVisibilityLocked();
@@ -7528,11 +7546,7 @@
parentAppBounds.width(), screenResolvedBounds.width());
} else {
float positionMultiplier =
- mWmService.mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
- positionMultiplier =
- (positionMultiplier < 0.0f || positionMultiplier > 1.0f)
- // Default to central position if invalid value is provided.
- ? 0.5f : positionMultiplier;
+ mLetterboxUiController.getHorizontalPositionMultiplier(newParentConfiguration);
offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width())
* positionMultiplier);
}
@@ -7549,6 +7563,15 @@
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
+ void recomputeConfiguration() {
+ onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ }
+
+ boolean isInTransition() {
+ return mAtmService.getTransitionController().inTransition() // Shell transitions.
+ || isAnimating(PARENTS | TRANSITION); // Legacy transitions.
+ }
+
/**
* Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
* orientation then aspect ratio restrictions are also already respected.
@@ -7651,6 +7674,7 @@
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
+
if (forcedOrientation == ORIENTATION_UNDEFINED
|| (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
return;
@@ -7702,10 +7726,8 @@
final Rect prevResolvedBounds = new Rect(resolvedBounds);
resolvedBounds.set(containingBounds);
- // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
- // set-fixed-orientation-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride =
- mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig);
final float desiredAspectRatio =
letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3b43e48..452291a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,7 @@
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -1573,11 +1573,7 @@
newTransition.setRemoteTransition(remoteTransition);
}
mService.getTransitionController().collect(r);
- // TODO(b/188669821): Remove when navbar reparenting moves to shell
- if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
- && r.getOptions().getTransientLaunch()) {
- mService.getTransitionController().setIsLegacyRecents();
- }
+ final boolean isTransient = r.getOptions() != null && r.getOptions().getTransientLaunch();
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
@@ -1625,6 +1621,11 @@
// it as an existence change.
mService.getTransitionController().collectExistenceChange(r);
}
+ if (isTransient) {
+ // `r` isn't guaranteed to be the actual relevant activity, so we must wait
+ // until after we launched to identify the relevant activity.
+ mService.getTransitionController().setTransientLaunch(mLastStartActivityRecord);
+ }
if (newTransition != null) {
mService.getTransitionController().requestStartTransition(newTransition,
mTargetTask, remoteTransition);
@@ -2698,7 +2699,11 @@
mStartActivity.appTimeTracker, DEFER_RESUME,
"bringingFoundTaskToFront");
mMovedToFront = !wasTopOfVisibleRootTask;
- } else {
+ } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ // Leaves reparenting pinned task operations to task organizer to make sure it
+ // dismisses pinned task properly.
+ // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
+ // to task organizer.
intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
mMovedToFront = true;
@@ -2722,6 +2727,17 @@
mTargetRootTask = intentActivity.getRootTask();
mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
+
+ // We need to check if there is a launch root task in TDA for this target root task.
+ // If it exist, we need to reparent target root task from TDA to launch root task.
+ final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
+ final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
+ mTargetRootTask.getActivityType(), null /** options */, null /** sourceTask */,
+ 0 /** launchFlags */);
+ if (launchRootTask != null && launchRootTask != mTargetRootTask) {
+ mTargetRootTask.reparent(launchRootTask, POSITION_TOP);
+ mTargetRootTask = launchRootTask;
+ }
}
private void resumeTargetRootTaskIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 5174a38..0ba77d8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -30,6 +30,7 @@
import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.RemoteException;
import android.service.voice.IVoiceInteractionSession;
import android.util.IntArray;
@@ -611,6 +612,14 @@
PackageConfigurationUpdater setNightMode(int nightMode);
/**
+ * Sets the app-specific locales for the application referenced by this updater.
+ * This setting is persisted and will overlay on top of the system locales for
+ * the said application.
+ * @return the current {@link PackageConfigurationUpdater} updated with the provided locale.
+ */
+ PackageConfigurationUpdater setLocales(LocaleList locales);
+
+ /**
* Commit changes.
*/
void commit();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 808d138..3f205c9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -652,16 +652,25 @@
*/
volatile int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+ /** Whether to keep higher priority to launch app while device is sleeping. */
+ private volatile boolean mRetainPowerModeAndTopProcessState;
+
+ /** The timeout to restore power mode if {@link #mRetainPowerModeAndTopProcessState} is set. */
+ private static final long POWER_MODE_UNKNOWN_VISIBILITY_TIMEOUT_MS = 1000;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({
POWER_MODE_REASON_START_ACTIVITY,
POWER_MODE_REASON_FREEZE_DISPLAY,
+ POWER_MODE_REASON_UNKNOWN_VISIBILITY,
POWER_MODE_REASON_ALL,
})
@interface PowerModeReason {}
static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+ /** @see UnknownAppVisibilityController */
+ static final int POWER_MODE_REASON_UNKNOWN_VISIBILITY = 1 << 2;
/** This can only be used by {@link #endLaunchPowerMode(int)}.*/
static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
@@ -946,7 +955,7 @@
setRecentTasks(new RecentTasks(this, mTaskSupervisor));
mVrController = new VrController(mGlobalLock);
mKeyguardController = mTaskSupervisor.getKeyguardController();
- mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue);
+ mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue, this);
}
public void onActivityManagerInternalAdded() {
@@ -4247,15 +4256,39 @@
}
void startLaunchPowerMode(@PowerModeReason int reason) {
- if (mPowerManagerInternal == null) return;
- mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ if (mPowerManagerInternal != null) {
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ }
mLaunchPowerModeReasons |= reason;
+ if ((reason & POWER_MODE_REASON_UNKNOWN_VISIBILITY) != 0) {
+ if (mRetainPowerModeAndTopProcessState) {
+ mH.removeMessages(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG);
+ }
+ mRetainPowerModeAndTopProcessState = true;
+ mH.sendEmptyMessageDelayed(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG,
+ POWER_MODE_UNKNOWN_VISIBILITY_TIMEOUT_MS);
+ Slog.d(TAG, "Temporarily retain top process state for launching app");
+ }
}
void endLaunchPowerMode(@PowerModeReason int reason) {
- if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+ if (mLaunchPowerModeReasons == 0) return;
mLaunchPowerModeReasons &= ~reason;
- if (mLaunchPowerModeReasons == 0) {
+
+ if ((mLaunchPowerModeReasons & POWER_MODE_REASON_UNKNOWN_VISIBILITY) != 0) {
+ boolean allResolved = true;
+ for (int i = mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+ allResolved &= mRootWindowContainer.getChildAt(i).mUnknownAppVisibilityController
+ .allResolved();
+ }
+ if (allResolved) {
+ mLaunchPowerModeReasons &= ~POWER_MODE_REASON_UNKNOWN_VISIBILITY;
+ mRetainPowerModeAndTopProcessState = false;
+ mH.removeMessages(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG);
+ }
+ }
+
+ if (mLaunchPowerModeReasons == 0 && mPowerManagerInternal != null) {
mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
}
}
@@ -5117,6 +5150,7 @@
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
+ static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5140,6 +5174,20 @@
}
}
break;
+ case END_POWER_MODE_UNKNOWN_VISIBILITY_MSG: {
+ synchronized (mGlobalLock) {
+ mRetainPowerModeAndTopProcessState = false;
+ endLaunchPowerMode(POWER_MODE_REASON_UNKNOWN_VISIBILITY);
+ if (mTopApp != null
+ && mTopProcessState == ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+ // Restore the scheduling group for sleeping.
+ mTopApp.updateProcessInfo(false /* updateServiceConnection */,
+ false /* activityChange */, true /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ }
+ }
+ }
+ break;
}
}
}
@@ -5458,6 +5506,11 @@
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
@Override
public int getTopProcessState() {
+ if (mRetainPowerModeAndTopProcessState) {
+ // There is a launching app while device may be sleeping, force the top state so
+ // the launching process can have top-app scheduling group.
+ return ActivityManager.PROCESS_STATE_TOP;
+ }
return mTopProcessState;
}
@@ -6516,7 +6569,8 @@
final class PackageConfigurationUpdaterImpl implements
ActivityTaskManagerInternal.PackageConfigurationUpdater {
private final int mPid;
- private int mNightMode;
+ private Integer mNightMode;
+ private LocaleList mLocales;
PackageConfigurationUpdaterImpl(int pid) {
mPid = pid;
@@ -6529,6 +6583,13 @@
}
@Override
+ public ActivityTaskManagerInternal.PackageConfigurationUpdater
+ setLocales(LocaleList locales) {
+ mLocales = locales;
+ return this;
+ }
+
+ @Override
public void commit() {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -6538,8 +6599,10 @@
Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
return;
}
- wpc.setOverrideNightMode(mNightMode);
- wpc.updateNightModeForAllActivities(mNightMode);
+ LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ mLocales, getGlobalConfiguration().getLocales());
+ wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+ wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6547,8 +6610,12 @@
}
}
- int getNightMode() {
+ Integer getNightMode() {
return mNightMode;
}
+
+ LocaleList getLocales() {
+ return mLocales;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 6fafc02..5a2cf17 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -28,6 +28,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.app.WindowConfigurationProto.WINDOWING_MODE;
+import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -39,6 +41,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.LocaleList;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -512,7 +515,7 @@
return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
}
- /** Returns the activity type associated with the the configuration container. */
+ /** Returns the activity type associated with the configuration container. */
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
return mFullConfiguration.windowConfiguration.getActivityType();
@@ -546,20 +549,48 @@
}
/**
+ * Applies app-specific nightMode and {@link LocaleList} on requested configuration.
+ * @return true if any of the requested configuration has been updated.
+ */
+ public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales) {
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig,
+ nightMode);
+ boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
+ locales);
+ if (newNightModeSet || newLocalesSet) {
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
+ }
+ return newNightModeSet || newLocalesSet;
+ }
+
+ /**
* Overrides the night mode applied to this ConfigurationContainer.
* @return true if the nightMode has been changed.
*/
- public boolean setOverrideNightMode(int nightMode) {
+ private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) {
final int currentUiMode = mRequestedOverrideConfiguration.uiMode;
final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentNightMode == validNightMode) {
return false;
}
- mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
- mRequestsTmpConfig.uiMode = validNightMode
+ requestsTmpConfig.uiMode = validNightMode
| (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
- onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
+ return true;
+ }
+
+ /**
+ * Overrides the locales applied to this ConfigurationContainer.
+ * @return true if the LocaleList has been changed.
+ */
+ private boolean setOverrideLocales(Configuration requestsTmpConfig,
+ @NonNull LocaleList overrideLocales) {
+ if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) {
+ return false;
+ }
+ requestsTmpConfig.setLocales(overrideLocales);
+ requestsTmpConfig.userSetLocale = true;
return true;
}
@@ -666,22 +697,40 @@
@CallSuper
protected void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
- // Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel != WindowTraceLogLevel.ALL && !mHasOverrideConfiguration) {
- return;
+ final long token = proto.start(fieldId);
+
+ if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
+ mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
+ logLevel == WindowTraceLogLevel.CRITICAL);
}
- final long token = proto.start(fieldId);
- mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
- logLevel == WindowTraceLogLevel.CRITICAL);
+ // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
+ // required to mitigate performance overhead
if (logLevel == WindowTraceLogLevel.ALL) {
mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
false /* critical */);
}
+
+ if (logLevel == WindowTraceLogLevel.TRIM) {
+ // Required for Fass to automatically detect pip transitions in Winscope traces
+ dumpDebugWindowingMode(proto);
+ }
+
proto.end(token);
}
+ private void dumpDebugWindowingMode(ProtoOutputStream proto) {
+ final long fullConfigToken = proto.start(FULL_CONFIGURATION);
+ final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
+
+ int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
+ proto.write(WINDOWING_MODE, windowingMode);
+
+ proto.end(windowConfigToken);
+ proto.end(fullConfigToken);
+ }
+
/**
* Dumps the names of this container children in the input print writer indenting each
* level with the input prefix.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 21fe96b..ea3b725 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -111,6 +111,7 @@
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET;
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
+import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
@@ -574,6 +575,7 @@
* Specifies the count to determine whether to defer updating the IME target until ready.
*/
private int mDeferUpdateImeTargetCount;
+ private boolean mUpdateImeRequestedWhileDeferred;
private MagnificationSpec mMagnificationSpec;
@@ -3225,10 +3227,15 @@
if (mCurrentFocus != null) {
mCurrentFocus.dumpDebug(proto, CURRENT_FOCUS, logLevel);
}
- if (mInsetsStateController != null
- && mInsetsStateController.getImeSourceProvider() != null) {
- mInsetsStateController.getImeSourceProvider().dumpDebug(proto,
- IME_INSETS_SOURCE_PROVIDER, logLevel);
+ if (mInsetsStateController != null) {
+ for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+ final InsetsSourceProvider provider = mInsetsStateController.peekSourceProvider(
+ type);
+ if (provider != null) {
+ provider.dumpDebug(proto, type == ITYPE_IME ? IME_INSETS_SOURCE_PROVIDER :
+ INSETS_SOURCE_PROVIDERS, logLevel);
+ }
+ }
}
proto.write(IME_POLICY, getImePolicy());
proto.end(token);
@@ -3723,6 +3730,7 @@
final WindowState curTarget = mImeLayeringTarget;
if (!canUpdateImeTarget()) {
if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Defer updating IME target");
+ mUpdateImeRequestedWhileDeferred = true;
return curTarget;
}
@@ -4977,6 +4985,9 @@
* Increment the deferral count to determine whether to update the IME target.
*/
void deferUpdateImeTarget() {
+ if (mDeferUpdateImeTargetCount == 0) {
+ mUpdateImeRequestedWhileDeferred = false;
+ }
mDeferUpdateImeTargetCount++;
}
@@ -4990,7 +5001,7 @@
}
mDeferUpdateImeTargetCount--;
- if (mDeferUpdateImeTargetCount == 0) {
+ if (mDeferUpdateImeTargetCount == 0 && mUpdateImeRequestedWhileDeferred) {
computeImeTarget(true /* updateImeTarget */);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 73d6cec..c9db14d 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -444,7 +444,9 @@
}
if (mDisplayContent.mFixedRotationTransitionListener
- .isTopFixedOrientationRecentsAnimating()) {
+ .isTopFixedOrientationRecentsAnimating()
+ // If screen is off or the device is going to sleep, then still allow to update.
+ && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
// During the recents animation, the closing app might still be considered on top.
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 18ea738b..aa257f8 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -464,7 +464,8 @@
if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) {
// Only allow the extras to be dispatched to a global-intercepting drag target
ClipData data = interceptsGlobalDrag ? mData.copyForTransferWithActivityInfo() : null;
- DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY,
+ DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED,
+ newWin.translateToWindowX(touchX), newWin.translateToWindowY(touchY),
data, false /* includeDragSurface */,
null /* dragAndDropPermission */);
try {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 4f6a693..cbefe7f 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -101,6 +101,24 @@
super.updateControlForTarget(target, force);
}
+ @Override
+ protected boolean updateClientVisibility(InsetsControlTarget caller) {
+ boolean changed = super.updateClientVisibility(caller);
+ if (changed && caller.getRequestedVisibility(mSource.getType())) {
+ reportImeDrawnForOrganizer(caller);
+ }
+ return changed;
+ }
+
+ private void reportImeDrawnForOrganizer(InsetsControlTarget caller) {
+ if (caller.getWindow() != null && caller.getWindow().getTask() != null) {
+ if (caller.getWindow().getTask().isOrganized()) {
+ mWin.mWmService.mAtmService.mTaskOrganizerController.reportImeDrawnOnTask(
+ caller.getWindow().getTask());
+ }
+ }
+ }
+
private void onSourceChanged() {
if (mLastSource.equals(mSource)) {
return;
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 6ce7b42..142d293 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -276,6 +276,10 @@
mChanged = true;
}
+ boolean isTrustedOverlay() {
+ return mHandle.trustedOverlay;
+ }
+
@Override
public String toString() {
return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f93e085..767e2c2 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -267,7 +267,7 @@
&& mWin.okToDisplay()) {
mWin.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
- mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
+ mSetLeashPositionConsumer.accept(mWin.getSyncTransaction());
}
}
if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
@@ -331,7 +331,7 @@
if (getSource().getType() == ITYPE_IME) {
setClientVisible(target.getRequestedVisibility(mSource.getType()));
}
- final Transaction t = mDisplayContent.getPendingTransaction();
+ final Transaction t = mDisplayContent.getSyncTransaction();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
ANIMATION_TYPE_INSETS_CONTROL);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4a8c36f..c630e91 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -109,13 +109,13 @@
}
/**
- * @return {@code true} for default display when AOD is showing. Otherwise, same as
- * {@link #isKeyguardOrAodShowing(int)}
+ * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same
+ * as {@link #isKeyguardOrAodShowing(int)}
* TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
*/
boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
if (displayId == DEFAULT_DISPLAY && mAodShowing) {
- return true;
+ return !mKeyguardGoingAway;
}
return isKeyguardOrAodShowing(displayId);
}
@@ -488,7 +488,7 @@
final KeyguardDisplayState state = getDisplayState(displayId);
if (isKeyguardUnoccludedOrAodShowing(displayId)) {
state.mSleepTokenAcquirer.acquire(displayId);
- } else if (!isKeyguardUnoccludedOrAodShowing(displayId)) {
+ } else {
state.mSleepTokenAcquirer.release(displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 5a249a5..c18c94d 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -19,14 +19,18 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Process;
+import android.view.GestureDetector;
import android.view.InputChannel;
+import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -65,6 +69,8 @@
// for overlaping an app window and letterbox surfaces.
private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
+ // Reachability gestures.
+ private final Runnable mDoubleTapCallback;
/**
* Constructs a Letterbox.
@@ -77,7 +83,8 @@
Supplier<Color> colorSupplier,
Supplier<Boolean> hasWallpaperBackgroundSupplier,
Supplier<Integer> blurRadiusSupplier,
- Supplier<Float> darkScrimAlphaSupplier) {
+ Supplier<Float> darkScrimAlphaSupplier,
+ Runnable doubleTapCallback) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
@@ -85,6 +92,7 @@
mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier;
mBlurRadiusSupplier = blurRadiusSupplier;
mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
+ mDoubleTapCallback = doubleTapCallback;
}
/**
@@ -231,18 +239,48 @@
return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get();
}
- private static class InputInterceptor {
- final InputChannel mClientChannel;
- final InputWindowHandle mWindowHandle;
- final InputEventReceiver mInputEventReceiver;
- final WindowManagerService mWmService;
- final IBinder mToken;
+ private final class TapEventReceiver extends InputEventReceiver {
+
+ private final GestureDetector mDoubleTapDetector;
+ private final DoubleTapListener mDoubleTapListener;
+
+ TapEventReceiver(InputChannel inputChannel, Context context) {
+ super(inputChannel, UiThread.getHandler().getLooper());
+ mDoubleTapListener = new DoubleTapListener();
+ mDoubleTapDetector = new GestureDetector(context, mDoubleTapListener);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ final MotionEvent motionEvent = (MotionEvent) event;
+ finishInputEvent(event, mDoubleTapDetector.onTouchEvent(motionEvent));
+ }
+ }
+
+ private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_UP) {
+ mDoubleTapCallback.run();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final class InputInterceptor {
+
+ private final InputChannel mClientChannel;
+ private final InputWindowHandle mWindowHandle;
+ private final InputEventReceiver mInputEventReceiver;
+ private final WindowManagerService mWmService;
+ private final IBinder mToken;
InputInterceptor(String namePrefix, WindowState win) {
mWmService = win.mWmService;
final String name = namePrefix + (win.mActivityRecord != null ? win.mActivityRecord : win);
mClientChannel = mWmService.mInputManager.createInputChannel(name);
- mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
+ mInputEventReceiver = new TapEventReceiver(mClientChannel, mWmService.mContext);
mToken = mClientChannel.getToken();
@@ -280,12 +318,6 @@
mInputEventReceiver.dispose();
mClientChannel.dispose();
}
-
- private static class SimpleInputReceiver extends InputEventReceiver {
- SimpleInputReceiver(InputChannel inputChannel) {
- super(inputChannel, UiThread.getHandler().getLooper());
- }
- }
}
private class LetterboxSurface {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 34b834b..76a098d 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -85,6 +85,26 @@
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
+ // Default horizontal position of a center of the letterboxed app window when reachability is
+ // enabled and an app is fullscreen in landscape device orientatio. 0 corresponds to the left
+ // side of the screen and 1.0 to the right side.
+ // It is used as a starting point for mLetterboxHorizontalMultiplierForReachability.
+ private float mDefaultPositionMultiplierForReachability;
+
+ // Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
+ // device orientation.
+ private boolean mIsReachabilityEnabled;
+
+ // Horizontal position of a center of the letterboxed app window. 0 corresponds to
+ // the left side of the screen and 1 to the right side. Keep it global to prevent
+ // "jumps" when switching between letterboxed apps. It's updated to reposition the app
+ // window in response to a double tap gesture (see LetterboxUiController#handleDoubleTap).
+ // Used in LetterboxUiController#getHorizontalPositionMultiplier which is called from
+ // ActivityRecord#updateResolvedBoundsHorizontalPosition.
+ // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
+ // Overview after changing position in another app.
+ private volatile float mLetterboxHorizontalMultiplierForReachability;
+
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
@@ -98,6 +118,11 @@
R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
R.dimen.config_letterboxHorizontalPositionMultiplier);
+ mIsReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsReachabilityEnabled);
+ mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
+ R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
+ mLetterboxHorizontalMultiplierForReachability = mDefaultPositionMultiplierForReachability;
}
/**
@@ -317,12 +342,12 @@
* in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
* or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
* right side.
- *
- * <p>This value can be outside of [0, 1] range so clients need to check and default to the
- * central position (0.5).
*/
float getLetterboxHorizontalPositionMultiplier() {
- return mLetterboxHorizontalPositionMultiplier;
+ return (mLetterboxHorizontalPositionMultiplier < 0.0f
+ || mLetterboxHorizontalPositionMultiplier > 1.0f)
+ // Default to central position if invalid value is provided.
+ ? 0.5f : mLetterboxHorizontalPositionMultiplier;
}
/**
@@ -344,4 +369,84 @@
com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
}
+ /*
+ * Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
+ * device orientation.
+ */
+ boolean getIsReachabilityEnabled() {
+ return mIsReachabilityEnabled;
+ }
+
+ /**
+ * Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in
+ * landscape device orientation.
+ */
+ void setIsReachabilityEnabled(boolean enabled) {
+ mIsReachabilityEnabled = enabled;
+ }
+
+ /**
+ * Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in
+ * landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}.
+ */
+ void resetIsReachabilityEnabled() {
+ mIsReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsReachabilityEnabled);
+ }
+
+ /*
+ * Gets default horizontal position of a center of the letterboxed app window when reachability
+ * is enabled specified in {@link
+ * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} or via an ADB command.
+ * 0 corresponds to the left side of the screen and 1 to the right side. The returned value is
+ * >= 0.0 and <= 1.0.
+ */
+ float getDefaultPositionMultiplierForReachability() {
+ return (mDefaultPositionMultiplierForReachability < 0.0f
+ || mDefaultPositionMultiplierForReachability > 1.0f)
+ // Default to a right position if invalid value is provided.
+ ? 1.0f : mDefaultPositionMultiplierForReachability;
+ }
+
+ /**
+ * Overrides default horizontal position of a center of the letterboxed app window when
+ * reachability is enabled. If given value < 0.0 or > 1.0, then it and a value of {@link
+ * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} are ignored and the right
+ * position (1.0) is used.
+ */
+ void setDefaultPositionMultiplierForReachability(float multiplier) {
+ mDefaultPositionMultiplierForReachability = multiplier;
+ }
+
+ /**
+ * Resets default horizontal position of a center of the letterboxed app window when
+ * reachability is enabled to {@link
+ * R.dimen.config_letterboxDefaultPositionMultiplierForReachability}.
+ */
+ void resetDefaultPositionMultiplierForReachability() {
+ mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
+ R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
+ }
+
+ /*
+ * Gets horizontal position of a center of the letterboxed app window when reachability
+ * is enabled specified. 0 corresponds to the left side of the screen and 1 to the right side.
+ *
+ * <p>The position multiplier is changed to a symmetrical value computed as (1 - current
+ * multiplier) after each double tap in the letterbox area.
+ */
+ float getHorizontalMultiplierForReachability() {
+ return mLetterboxHorizontalMultiplierForReachability;
+ }
+
+ /**
+ * Changes horizontal position of a center of the letterboxed app window to the opposite
+ * (1 - current multiplier) when reachability is enabled specified. 0 corresponds to the left
+ * side of the screen and 1 to the right side.
+ */
+ void flipHorizontalMultiplierForReachability() {
+ mLetterboxHorizontalMultiplierForReachability =
+ 1.0f - mLetterboxHorizontalMultiplierForReachability;
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index b6b8ad1..0d99bac 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,8 +16,12 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.computeAspectRatio;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
@@ -28,6 +32,8 @@
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -36,6 +42,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
@@ -138,7 +145,8 @@
this::getLetterboxBackgroundColor,
this::hasWallpaperBackgroudForLetterbox,
this::getLetterboxWallpaperBlurRadius,
- this::getLetterboxWallpaperDarkScrimAlpha);
+ this::getLetterboxWallpaperDarkScrimAlpha,
+ this::handleDoubleTap);
mLetterbox.attachInput(w);
}
mActivityRecord.getPosition(mTmpPoint);
@@ -158,6 +166,74 @@
}
}
+ float getHorizontalPositionMultiplier(Configuration parentConfiguration) {
+ // Don't check resolved configuration because it may not be updated yet during
+ // configuration change.
+ return isReachabilityEnabled(parentConfiguration)
+ // Using the last global dynamic position to avoid "jumps" when moving
+ // between apps or activities.
+ ? mLetterboxConfiguration.getHorizontalMultiplierForReachability()
+ : mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
+ }
+
+ float getFixedOrientationLetterboxAspectRatio(Configuration parentConfiguration) {
+ // Don't check resolved windowing mode because it may not be updated yet during
+ // configuration change.
+ if (!isReachabilityEnabled(parentConfiguration)) {
+ return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+
+ int dividerWindowWidth =
+ getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness);
+ int dividerInsets =
+ getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
+ int dividerSize = dividerWindowWidth - dividerInsets * 2;
+
+ // Getting the same aspect ratio that apps get in split screen.
+ Rect bounds = new Rect(parentConfiguration.windowConfiguration.getAppBounds());
+ bounds.inset(dividerSize, /* dy */ 0);
+ bounds.right = bounds.centerX();
+
+ return computeAspectRatio(bounds);
+ }
+
+ Resources getResources() {
+ return mActivityRecord.mWmService.mContext.getResources();
+ }
+
+ private void handleDoubleTap() {
+ if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ return;
+ }
+
+ mLetterboxConfiguration.flipHorizontalMultiplierForReachability();
+
+ // TODO(197549949): Add animation for transition.
+ mActivityRecord.recomputeConfiguration();
+ }
+
+ /**
+ * Whether reachability is enabled for an activity in the curren configuration.
+ *
+ * <p>Conditions that needs to be met:
+ * <ul>
+ * <li>Activity is portrait-only.
+ * <li>Fullscreen window in landscape device orientation.
+ * <li>Reachability is enabled.
+ * </ul>
+ */
+ private boolean isReachabilityEnabled(Configuration parentConfiguration) {
+ return mLetterboxConfiguration.getIsReachabilityEnabled()
+ && parentConfiguration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN
+ && parentConfiguration.orientation == ORIENTATION_LANDSCAPE
+ && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT;
+ }
+
+ private boolean isReachabilityEnabled() {
+ return isReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+ }
+
@VisibleForTesting
boolean shouldShowLetterboxUi(WindowState mainWindow) {
return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
@@ -285,7 +361,7 @@
}
pw.println(prefix + " letterboxReason=" + getLetterboxReasonString(mainWin));
- pw.println(prefix + " letterboxAspectRatio="
+ pw.println(prefix + " activityAspectRatio="
+ mActivityRecord.computeAspectRatio(mActivityRecord.getBounds()));
boolean shouldShowLetterboxUi = shouldShowLetterboxUi(mainWin);
@@ -308,8 +384,13 @@
pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
+ getLetterboxWallpaperBlurRadius());
}
+
+ pw.println(prefix + " isReachabilityEnabled=" + isReachabilityEnabled());
pw.println(prefix + " letterboxHorizontalPositionMultiplier="
- + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ + getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
+ pw.println(prefix + " fixedOrientationLetterboxAspectRatio="
+ + getFixedOrientationLetterboxAspectRatio(
+ mActivityRecord.getParent().getConfiguration()));
}
/**
diff --git a/services/core/java/com/android/server/wm/LocaleOverlayHelper.java b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java
new file mode 100644
index 0000000..a1a01db
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import android.os.LocaleList;
+
+import java.util.Locale;
+
+/**
+ * Static utilities to overlay locales on top of another localeList.
+ *
+ * <p>This is used to overlay application-specific locales in
+ * {@link com.android.server.wm.ActivityTaskManagerInternal.PackageConfigurationUpdater} on top of
+ * system locales.
+ */
+final class LocaleOverlayHelper {
+
+ /**
+ * Combines the overlay locales and base locales.
+ * @return the combined {@link LocaleList} if the overlay locales is not empty/null else
+ * returns the empty/null LocaleList.
+ */
+ static LocaleList combineLocalesIfOverlayExists(LocaleList overlayLocales,
+ LocaleList baseLocales) {
+ if (overlayLocales == null || overlayLocales.isEmpty()) {
+ return overlayLocales;
+ }
+ return combineLocales(overlayLocales, baseLocales);
+ }
+
+ /**
+ * Creates a combined {@link LocaleList} by placing overlay locales before base locales and
+ * dropping duplicates from the base locales.
+ */
+ private static LocaleList combineLocales(LocaleList overlayLocales, LocaleList baseLocales) {
+ Locale[] combinedLocales = new Locale[overlayLocales.size() + baseLocales.size()];
+ for (int i = 0; i < overlayLocales.size(); i++) {
+ combinedLocales[i] = overlayLocales.get(i);
+ }
+ for (int i = 0; i < baseLocales.size(); i++) {
+ combinedLocales[i + overlayLocales.size()] = baseLocales.get(i);
+ }
+ // Constructor of {@link LocaleList} removes duplicates
+ return new LocaleList(combinedLocales);
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 1552a96..505c4be 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.os.Environment;
+import android.os.LocaleList;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -54,12 +55,14 @@
private static final String TAG_CONFIG = "config";
private static final String ATTR_PACKAGE_NAME = "package_name";
private static final String ATTR_NIGHT_MODE = "night_mode";
+ private static final String ATTR_LOCALES = "locale_list";
private static final String PACKAGE_DIRNAME = "package_configs";
private static final String SUFFIX_FILE_NAME = "_config.xml";
private final PersisterQueue mPersisterQueue;
private final Object mLock = new Object();
+ private final ActivityTaskManagerService mAtm;
@GuardedBy("mLock")
private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite =
@@ -72,8 +75,9 @@
return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME);
}
- PackageConfigPersister(PersisterQueue queue) {
+ PackageConfigPersister(PersisterQueue queue, ActivityTaskManagerService atm) {
mPersisterQueue = queue;
+ mAtm = atm;
}
@GuardedBy("mLock")
@@ -100,7 +104,8 @@
final TypedXmlPullParser in = Xml.resolvePullParser(is);
int event;
String packageName = null;
- int nightMode = MODE_NIGHT_AUTO;
+ Integer nightMode = null;
+ LocaleList locales = null;
while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
&& event != XmlPullParser.END_TAG) {
final String name = in.getName();
@@ -120,6 +125,9 @@
case ATTR_NIGHT_MODE:
nightMode = Integer.parseInt(attrValue);
break;
+ case ATTR_LOCALES:
+ locales = LocaleList.forLanguageTags(attrValue);
+ break;
}
}
}
@@ -130,6 +138,7 @@
final PackageConfigRecord initRecord =
findRecordOrCreate(mModified, packageName, userId);
initRecord.mNightMode = nightMode;
+ initRecord.mLocales = locales;
if (DEBUG) {
Slog.d(TAG, "loadPackages: load one package " + initRecord);
}
@@ -155,7 +164,9 @@
"updateConfigIfNeeded record " + container + " find? " + modifiedRecord);
}
if (modifiedRecord != null) {
- container.setOverrideNightMode(modifiedRecord.mNightMode);
+ container.applyAppSpecificConfig(modifiedRecord.mNightMode,
+ LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales()));
}
}
}
@@ -165,10 +176,16 @@
ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) {
synchronized (mLock) {
PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
- record.mNightMode = impl.getNightMode();
-
- if (record.isResetNightMode()) {
- removePackage(record.mName, record.mUserId);
+ if (impl.getNightMode() != null) {
+ record.mNightMode = impl.getNightMode();
+ }
+ if (impl.getLocales() != null) {
+ record.mLocales = impl.getLocales();
+ }
+ if ((record.mNightMode == null || record.isResetNightMode())
+ && (record.mLocales == null || record.mLocales.isEmpty())) {
+ // if all values default to system settings, we can remove the package.
+ removePackage(packageName, userId);
} else {
final PackageConfigRecord pendingRecord =
findRecord(mPendingWrite, record.mName, record.mUserId);
@@ -179,10 +196,11 @@
} else {
writeRecord = pendingRecord;
}
- if (writeRecord.mNightMode == record.mNightMode) {
+
+ if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
return;
}
- writeRecord.mNightMode = record.mNightMode;
+
if (DEBUG) {
Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
}
@@ -191,6 +209,22 @@
}
}
+ private boolean updateNightMode(PackageConfigRecord record, PackageConfigRecord writeRecord) {
+ if (record.mNightMode == null || record.mNightMode.equals(writeRecord.mNightMode)) {
+ return false;
+ }
+ writeRecord.mNightMode = record.mNightMode;
+ return true;
+ }
+
+ private boolean updateLocales(PackageConfigRecord record, PackageConfigRecord writeRecord) {
+ if (record.mLocales == null || record.mLocales.equals(writeRecord.mLocales)) {
+ return false;
+ }
+ writeRecord.mLocales = record.mLocales;
+ return true;
+ }
+
@GuardedBy("mLock")
void removeUser(int userId) {
synchronized (mLock) {
@@ -210,7 +244,7 @@
@GuardedBy("mLock")
void onPackageUninstall(String packageName) {
synchronized (mLock) {
- for (int i = mModified.size() - 1; i > 0; i--) {
+ for (int i = mModified.size() - 1; i >= 0; i--) {
final int userId = mModified.keyAt(i);
removePackage(packageName, userId);
}
@@ -242,7 +276,8 @@
static class PackageConfigRecord {
final String mName;
final int mUserId;
- int mNightMode;
+ Integer mNightMode;
+ LocaleList mLocales;
PackageConfigRecord(String name, int userId) {
mName = name;
@@ -256,7 +291,7 @@
@Override
public String toString() {
return "PackageConfigRecord package name: " + mName + " userId " + mUserId
- + " nightMode " + mNightMode;
+ + " nightMode " + mNightMode + " locales " + mLocales;
}
}
@@ -369,7 +404,13 @@
}
xmlSerializer.startTag(null, TAG_CONFIG);
xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName);
- xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode);
+ if (mRecord.mNightMode != null) {
+ xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode);
+ }
+ if (mRecord.mLocales != null) {
+ xmlSerializer.attribute(null, ATTR_LOCALES, mRecord.mLocales
+ .toLanguageTags());
+ }
xmlSerializer.endTag(null, TAG_CONFIG);
xmlSerializer.endDocument();
xmlSerializer.flush();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6c2322b..c48dba4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3545,14 +3545,7 @@
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
- final boolean sendPowerModeLaunch;
-
- if (forceSend) {
- sendPowerModeLaunch = true;
- } else if (targetActivity == null || targetActivity.app == null) {
- // Set power mode if we don't know what we're launching yet.
- sendPowerModeLaunch = true;
- } else {
+ if (!forceSend && targetActivity != null && targetActivity.app != null) {
// Set power mode when the activity's process is different than the current top resumed
// activity on all display areas, or if there are no resumed activities in the system.
boolean[] noResumedActivities = {true};
@@ -3568,13 +3561,28 @@
!resumedActivityProcess.equals(targetActivity.app);
}
});
- sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
+ if (!noResumedActivities[0] && !allFocusedProcessesDiffer[0]) {
+ // All focused activities are resumed and the process of the target activity is
+ // the same as them, e.g. delivering new intent to the current top.
+ return;
+ }
}
- if (sendPowerModeLaunch) {
- mService.startLaunchPowerMode(
- ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
+ int reason = ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY;
+ // If the activity is launching while keyguard is locked (including occluded), the activity
+ // may be visible until its first relayout is done (e.g. apply show-when-lock flag). To
+ // avoid power mode from being cleared before that, add a special reason to consider whether
+ // the unknown visibility is resolved. The case from SystemUI is excluded because it should
+ // rely on keyguard-going-away.
+ if (mService.mKeyguardController.isKeyguardLocked() && targetActivity != null
+ && !targetActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_SYSTEMUI)) {
+ final ActivityOptions opts = targetActivity.getOptions();
+ if (opts == null || opts.getSourceInfo() == null
+ || opts.getSourceInfo().type != ActivityOptions.SourceInfo.TYPE_LOCKSCREEN) {
+ reason |= ActivityTaskManagerService.POWER_MODE_REASON_UNKNOWN_VISIBILITY;
+ }
}
+ mService.startLaunchPowerMode(reason);
}
// TODO(b/191434136): handle this properly when we add multi-window support on secondary
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e6e51b8..9ccf56d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1606,9 +1606,15 @@
} else {
forAllActivities((r) -> {
if (r.finishing) return;
- // TODO: figure-out how to avoid object creation due to capture of reason variable.
- r.finishIfPossible(Activity.RESULT_CANCELED,
- null /* resultData */, null /* resultGrants */, reason, false /* oomAdj */);
+ // Prevent the transition from being executed too early if the top activity is
+ // resumed but the mVisibleRequested of any other activity is true, the transition
+ // should wait until next activity resumed.
+ if (r.isState(RESUMED) || (r.isVisible()
+ && !mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CLOSE))) {
+ r.finishIfPossible(reason, false /* oomAdj */);
+ } else {
+ r.destroyIfPossible(reason);
+ }
});
}
}
@@ -3515,6 +3521,7 @@
final WindowState mainWindow = activity.findMainWindow();
if (mainWindow != null) {
info.mainWindowLayoutParams = mainWindow.getAttrs();
+ info.requestedVisibilities.set(mainWindow.getRequestedVisibilities());
}
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
@@ -4298,7 +4305,7 @@
/**
* @return true if the task is currently focused.
*/
- private boolean isFocused() {
+ boolean isFocused() {
if (mDisplayContent == null || mDisplayContent.mFocusedApp == null) {
return false;
}
@@ -4360,6 +4367,10 @@
* @param hasFocus
*/
void onAppFocusChanged(boolean hasFocus) {
+ final ActivityRecord topAct = getTopVisibleActivity();
+ if (topAct != null && (topAct.mStartingData instanceof SnapshotStartingData)) {
+ topAct.removeStartingWindowIfNeeded();
+ }
updateShadowsRadius(hasFocus, getSyncTransaction());
dispatchTaskInfoChangedIfNeeded(false /* force */);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c7ca180..4a1a922 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1095,29 +1095,27 @@
return rootTask;
}
} else if (candidateTask != null) {
- final Task rootTask = candidateTask;
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options,
sourceTask, launchFlags);
-
if (launchRootTask != null) {
- if (rootTask.getParent() == null) {
- launchRootTask.addChild(rootTask, position);
- } else if (rootTask.getParent() != launchRootTask) {
- rootTask.reparent(launchRootTask, position);
+ if (candidateTask.getParent() == null) {
+ launchRootTask.addChild(candidateTask, position);
+ } else if (candidateTask.getParent() != launchRootTask) {
+ candidateTask.reparent(launchRootTask, position);
}
- } else if (rootTask.getDisplayArea() != this || !rootTask.isRootTask()) {
- if (rootTask.getParent() == null) {
- addChild(rootTask, position);
+ } else if (candidateTask.getDisplayArea() != this || !candidateTask.isRootTask()) {
+ if (candidateTask.getParent() == null) {
+ addChild(candidateTask, position);
} else {
- rootTask.reparent(this, onTop);
+ candidateTask.reparent(this, onTop);
}
}
// Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
if (candidateTask.getWindowingMode() != windowingMode) {
candidateTask.setWindowingMode(windowingMode);
}
- return rootTask;
+ return candidateTask.getRootTask();
}
return new Task.Builder(mAtmService)
.setWindowingMode(windowingMode)
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ca1d83e..abe95fa 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -447,7 +447,7 @@
return this;
}
- TaskFragment parentTaskFragment = getParent().asTaskFragment();
+ TaskFragment parentTaskFragment = getParent() != null ? getParent().asTaskFragment() : null;
return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fa7b276..28beaf3 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -478,7 +478,7 @@
if (launchTheme != 0) {
info.splashScreenThemeResId = launchTheme;
}
- info.mTaskSnapshot = taskSnapshot;
+ info.taskSnapshot = taskSnapshot;
// make this happen prior than prepare surface
try {
lastOrganizer.addStartingWindow(info, activity.token);
@@ -703,6 +703,17 @@
mPendingTaskEvents.clear();
}
+ void reportImeDrawnOnTask(Task task) {
+ final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
+ if (state != null) {
+ try {
+ state.mOrganizer.mTaskOrganizer.onImeDrawnOnTask(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onImeDrawnOnTask callback", e);
+ }
+ }
+ }
+
void onTaskInfoChanged(Task task, boolean force) {
if (!task.mTaskAppearedSent) {
// Skip if task still not appeared.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 6b93364..1a46d0f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -148,6 +148,9 @@
*/
private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
+ /** Set of transient activities (lifecycle initially tied to this transition). */
+ private ArraySet<ActivityRecord> mTransientLaunches = null;
+
/** Custom activity-level animation options and callbacks. */
private TransitionInfo.AnimationOptions mOverrideOptions;
private IRemoteCallback mClientAnimationStartCallback = null;
@@ -174,6 +177,20 @@
mFlags |= flag;
}
+ /** Records an activity as transient-launch. This activity must be already collected. */
+ void setTransientLaunch(@NonNull ActivityRecord activity) {
+ if (mTransientLaunches == null) {
+ mTransientLaunches = new ArraySet<>();
+ }
+ mTransientLaunches.add(activity);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ + "transient-launch", mSyncId, activity);
+ }
+
+ boolean isTransientLaunch(@NonNull ActivityRecord activity) {
+ return mTransientLaunches != null && mTransientLaunches.contains(activity);
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 69e6a54..c1d0f80 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -185,6 +186,20 @@
return false;
}
+ /**
+ * @return {@code true} if {@param ar} is part of a transient-launch activity in an active
+ * transition.
+ */
+ boolean isTransientLaunch(@NonNull ActivityRecord ar) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientLaunch(ar)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientLaunch(ar)) return true;
+ }
+ return false;
+ }
+
@WindowManager.TransitionType
int getCollectingTransitionType() {
return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
@@ -331,13 +346,18 @@
}
/**
- * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
- * behaviors.
- * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ * Record that the launch of {@param activity} is transient (meaning its lifecycle is currently
+ * tied to the transition).
*/
- void setIsLegacyRecents() {
+ void setTransientLaunch(@NonNull ActivityRecord activity) {
if (mCollectingTransition == null) return;
- mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ mCollectingTransition.setTransientLaunch(activity);
+
+ // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ // Also interpret HOME transient launch as recents
+ if (activity.getActivityType() == ACTIVITY_TYPE_HOME) {
+ mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ }
}
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 31bf49d..c78e162 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -355,6 +355,10 @@
throw new IllegalArgumentException("reparent: can't reparent to null " + this);
}
+ if (newParent == this) {
+ throw new IllegalArgumentException("Can not reparent to itself " + this);
+ }
+
final WindowContainer oldParent = mParent;
if (mParent == newParent) {
throw new IllegalArgumentException("WC=" + this + " already child of " + mParent);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8ac32ab..5ef7428 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7709,7 +7709,7 @@
final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
- return true;
+ return currentFocus.canBeImeTarget();
}
}
return false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 1d1cb70..47d7f03 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -745,7 +745,7 @@
return 0;
}
- private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
final float multiplier;
try {
String arg = getNextArgRequired();
@@ -764,6 +764,49 @@
return 0;
}
+ private int runSetLetterboxIsReachabilityEnabled(PrintWriter pw) throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsReachabilityEnabled(enabled);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxDefaultPositionMultiplierForReachability(PrintWriter pw)
+ throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: multiplier should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setDefaultPositionMultiplierForReachability(multiplier);
+ }
+ return 0;
+ }
+
private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -793,7 +836,13 @@
runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
break;
case "--horizontalPositionMultiplier":
- runSeLetterboxHorizontalPositionMultiplier(pw);
+ runSetLetterboxHorizontalPositionMultiplier(pw);
+ break;
+ case "--isReachabilityEnabled":
+ runSetLetterboxIsReachabilityEnabled(pw);
+ break;
+ case "--defaultPositionMultiplierReachability":
+ runSetLetterboxDefaultPositionMultiplierForReachability(pw);
break;
default:
getErrPrintWriter().println(
@@ -833,6 +882,12 @@
case "horizontalPositionMultiplier":
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
break;
+ case "isReachabilityEnabled":
+ mLetterboxConfiguration.getIsReachabilityEnabled();
+ break;
+ case "defaultPositionMultiplierForReachability":
+ mLetterboxConfiguration.getDefaultPositionMultiplierForReachability();
+ break;
default:
getErrPrintWriter().println(
"Error: Unrecognized letterbox style option: " + arg);
@@ -926,6 +981,8 @@
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mLetterboxConfiguration.resetIsReachabilityEnabled();
+ mLetterboxConfiguration.resetDefaultPositionMultiplierForReachability();
}
}
@@ -937,6 +994,10 @@
+ mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
pw.println("Aspect ratio: "
+ mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+ pw.println("Is reachability enabled: "
+ + mLetterboxConfiguration.getIsReachabilityEnabled());
+ pw.println("Default position multiplier for reachability: "
+ + mLetterboxConfiguration.getDefaultPositionMultiplierForReachability());
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
@@ -1071,9 +1132,18 @@
pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" --isReachabilityEnabled [true|1|false|0]");
+ pw.println(" Whether reachability repositioning is allowed for letterboxed");
+ pw.println(" fullscreen apps in landscape device orientation.");
+ pw.println(" --defaultPositionMultiplierReachability multiplier");
+ pw.println(" Default horizontal position of app window center when reachability is");
+ pw.println(" enabled. If multiplier < 0.0 or > 1, both it and ");
+ pw.println(" R.dimen.config_letterboxDefaultPositionMultiplierForReachability");
+ pw.println(" are ignored and right position (1.0) is used.");
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
- pw.println(" |horizontalPositionMultiplier]");
+ pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled");
+ pw.println(" |defaultPositionMultiplierForReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
pw.println(" If no arguments provided, all values will be reset.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8bcd62d..834b6e6 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -733,18 +733,18 @@
tf1.setAdjacentTaskFragment(tf2);
final Bundle bundle = hop.getLaunchOptions();
- final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions =
- bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions(
+ final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
+ bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
bundle) : null;
- if (adjacentOptions == null) {
+ if (adjacentParams == null) {
break;
}
tf1.setDelayLastActivityRemoval(
- adjacentOptions.isDelayPrimaryLastActivityRemoval());
+ adjacentParams.shouldDelayPrimaryLastActivityRemoval());
if (tf2 != null) {
tf2.setDelayLastActivityRemoval(
- adjacentOptions.isDelaySecondaryLastActivityRemoval());
+ adjacentParams.shouldDelaySecondaryLastActivityRemoval());
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 056e17d9..5b73de7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -57,6 +57,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -817,10 +818,13 @@
return false;
}
- void updateNightModeForAllActivities(int nightMode) {
+ // TODO(b/199277065): Re-assess how app-specific locales are applied based on UXR
+ // TODO(b/199277729): Consider whether we need to add special casing for edge cases like
+ // activity-embeddings etc.
+ void updateAppSpecificSettingsForAllActivities(Integer nightMode, LocaleList localesOverride) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
- if (r.setOverrideNightMode(nightMode) && r.mVisibleRequested) {
+ if (r.applyAppSpecificConfig(nightMode, localesOverride) && r.mVisibleRequested) {
r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f848c22..c30db19 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -372,6 +372,14 @@
private boolean mRedrawForSyncReported;
/**
+ * {@code true} when the client was still drawing for sync when the sync-set was finished or
+ * cancelled. This can happen if the window goes away during a sync. In this situation we need
+ * to make sure to still apply the postDrawTransaction when it finishes to prevent the client
+ * from getting stuck in a bad state.
+ */
+ boolean mClientWasDrawingForSync = false;
+
+ /**
* Special mode that is intended only for the rounded corner overlay: during rotation
* transition, we un-rotate the window token such that the window appears as it did before the
* rotation.
@@ -1980,7 +1988,7 @@
final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
- && (atoken == null || atoken.mVisibleRequested)
+ && (atoken == null || atoken.isVisible())
&& !mAnimatingExit && !mDestroying;
}
@@ -2706,6 +2714,13 @@
}
}
+ // Don't allow transient-launch activities to take IME.
+ if (rootTask != null && mActivityRecord != null
+ && mWmService.mAtmService.getTransitionController().isTransientLaunch(
+ mActivityRecord)) {
+ return false;
+ }
+
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "isVisibleOrAdding " + this + ": " + isVisibleOrAdding());
if (!isVisibleOrAdding()) {
@@ -6002,6 +6017,14 @@
return super.isSyncFinished();
}
+ @Override
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mRedrawForSyncReported) {
+ mClientWasDrawingForSync = true;
+ }
+ super.finishSync(outMergedTransaction, cancel);
+ }
+
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
if (mOrientationChangeRedrawRequestTime > 0) {
final long duration =
@@ -6017,8 +6040,11 @@
}
executeDrawHandlers(postDrawTransaction);
+
+ final boolean applyPostDrawNow = mClientWasDrawingForSync && postDrawTransaction != null;
+ mClientWasDrawingForSync = false;
if (!onSyncFinishedDrawing()) {
- return mWinAnimator.finishDrawingLocked(postDrawTransaction);
+ return mWinAnimator.finishDrawingLocked(postDrawTransaction, applyPostDrawNow);
}
if (mActivityRecord != null
@@ -6032,7 +6058,7 @@
mSyncTransaction.merge(postDrawTransaction);
}
- mWinAnimator.finishDrawingLocked(null);
+ mWinAnimator.finishDrawingLocked(null, false /* forceApplyNow */);
// We always want to force a traversal after a finish draw for blast sync.
return true;
}
@@ -6231,4 +6257,8 @@
scheduleAnimation();
return true;
}
+
+ boolean isTrustedOverlay() {
+ return mInputWindowHandle.isTrustedOverlay();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 81f06d9..0e110b6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -228,7 +228,8 @@
}
}
- boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
+ boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
+ boolean forceApplyNow) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
if (startingWindow) {
@@ -253,11 +254,11 @@
// If there is no surface, the last draw was for the previous surface. We don't want to
// wait until the new surface is shown and instead just apply the transaction right
// away.
- if (mLastHidden && mDrawState != NO_SURFACE) {
+ if (mLastHidden && mDrawState != NO_SURFACE && !forceApplyNow) {
mPostDrawTransaction.merge(postDrawTransaction);
layoutNeeded = true;
} else {
- postDrawTransaction.apply();
+ mWin.getSyncTransaction().merge(postDrawTransaction);
}
}
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index e319e3f..4190a91 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -55,6 +55,7 @@
#define SYNC_RECEIVED_WHILE_FROZEN (1)
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
+#define TXNS_PENDING_WHILE_FROZEN (4)
namespace android {
@@ -232,17 +233,20 @@
compactProcessOrFallback(pid, compactionFlags);
}
-static void com_android_server_am_CachedAppOptimizer_freezeBinder(
+static jint com_android_server_am_CachedAppOptimizer_freezeBinder(
JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
- if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) {
+ jint retVal = IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */);
+ if (retVal != 0 && retVal != -EAGAIN) {
jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
}
+
+ return retVal;
}
static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
jobject clazz, jint pid) {
- bool syncReceived = false, asyncReceived = false;
+ uint32_t syncReceived = 0, asyncReceived = 0;
int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
@@ -252,13 +256,12 @@
jint retVal = 0;
- if(syncReceived) {
- retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
- }
-
- if(asyncReceived) {
- retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
- }
+ // bit 0 of sync_recv goes to bit 0 of retVal
+ retVal |= syncReceived & SYNC_RECEIVED_WHILE_FROZEN;
+ // bit 0 of async_recv goes to bit 1 of retVal
+ retVal |= (asyncReceived << 1) & ASYNC_RECEIVED_WHILE_FROZEN;
+ // bit 1 of sync_recv goes to bit 2 of retVal
+ retVal |= (syncReceived << 1) & TXNS_PENDING_WHILE_FROZEN;
return retVal;
}
@@ -278,7 +281,7 @@
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
- {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+ {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
{"getBinderFreezeInfo", "(I)I",
(void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
{"getFreezerCheckPath", "()Ljava/lang/String;",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 8ea21ec..a301799 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -46,6 +46,12 @@
@GuardedBy("mLock")
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+ /** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}.
+ *
+ * <p>For users affiliated with the device, they inherit the policy from {@code DO} so
+ * it will map to the {@code DO}'s policy. Otherwise it will map to the admin of the requesting
+ * user.
+ */
@GuardedBy("mLock")
private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
@@ -102,17 +108,16 @@
}
@Override
- public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) {
+ public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userId) {
synchronized (mLock) {
- return mCanGrantSensorsPermissions.get(userHandle, false);
+ return mCanGrantSensorsPermissions.get(userId, false);
}
}
/** Sets ahmin control over permission grants for user. */
- public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle,
- boolean canGrant) {
+ public void setAdminCanGrantSensorsPermissions(@UserIdInt int userId, boolean canGrant) {
synchronized (mLock) {
- mCanGrantSensorsPermissions.put(userHandle, canGrant);
+ mCanGrantSensorsPermissions.put(userId, canGrant);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5b205cd..a0afb17 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1150,8 +1150,9 @@
&& packageName.equals(service.getCredentialManagementAppPackageName())) {
service.removeCredentialManagementApp();
}
- } catch (RemoteException | InterruptedException | IllegalStateException e) {
- Slogf.e(LOG_TAG, "Unable to remove the credential management app");
+ } catch (RemoteException | InterruptedException | IllegalStateException
+ | AssertionError e) {
+ Slogf.e(LOG_TAG, "Unable to remove the credential management app", e);
}
});
}
@@ -1987,13 +1988,12 @@
final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
ActiveAdmin admin = policy.mAdminMap.get(adminComponent);
- if (admin == null) {
+ // Throwing combined exception message for both the cases here, because from different
+ // security exceptions it could be deduced if particular package is admin package.
+ if (admin == null || admin.getUid() != callerUid) {
throw new SecurityException(String.format(
- "No active admin for %s", adminComponent));
- }
- if (admin.getUid() != callerUid) {
- throw new SecurityException(String.format(
- "Admin %s is not owned by uid %d", adminComponent, callerUid));
+ "Admin %s does not exist or is not owned by uid %d", adminComponent,
+ callerUid));
}
if (callerPackage != null) {
Preconditions.checkArgument(callerPackage.equals(adminComponent.getPackageName()));
@@ -3624,6 +3624,9 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(
+ isCallingFromPackage(adminReceiver.getPackageName(), caller.getUid())
+ || isSystemUid(caller));
synchronized (getLockObject()) {
ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
@@ -3786,7 +3789,8 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
+ final CallerIdentity caller = hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)
+ ? getCallerIdentity() : getCallerIdentity(adminReceiver);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
@@ -3803,8 +3807,7 @@
+ adminReceiver);
return;
}
- Preconditions.checkCallAuthorization(admin.getUid() == caller.getUid()
- || hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
}
@@ -8150,17 +8153,16 @@
*/
@Override
public boolean getCameraDisabled(ComponentName who, int userHandle, boolean parent) {
- return getCameraDisabled(who, userHandle, /* mergeDeviceOwnerRestriction= */ true, parent);
- }
-
- private boolean getCameraDisabled(ComponentName who, int userHandle,
- boolean mergeDeviceOwnerRestriction, boolean parent) {
if (!mHasFeature) {
return false;
}
+
+ final CallerIdentity caller = getCallerIdentity(who);
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+
if (parent) {
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
+ isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()));
}
synchronized (getLockObject()) {
@@ -8169,17 +8171,15 @@
return (admin != null) && admin.disableCamera;
}
// First, see if DO has set it. If so, it's device-wide.
- if (mergeDeviceOwnerRestriction) {
- final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- if (deviceOwner != null && deviceOwner.disableCamera) {
- return true;
- }
+ final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner != null && deviceOwner.disableCamera) {
+ return true;
}
final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
// Return the strictest policy across all participating admins.
List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(affectedUserId);
// Determine whether or not the device camera is disabled for any active admins.
- for (ActiveAdmin admin: admins) {
+ for (ActiveAdmin admin : admins) {
if (admin.disableCamera) {
return true;
}
@@ -8243,6 +8243,9 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(
+ who == null || isCallingFromPackage(who.getPackageName(), caller.getUid())
+ || isSystemUid(caller));
final long ident = mInjector.binderClearCallingIdentity();
try {
@@ -9141,9 +9144,7 @@
}
/**
- * Returns the ActiveAdmin associated wit the PO or DO on the given user.
- * @param userHandle
- * @return
+ * Returns the ActiveAdmin associated with the PO or DO on the given user.
*/
private @Nullable ActiveAdmin getDeviceOrProfileOwnerAdminLocked(int userHandle) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
@@ -12708,74 +12709,21 @@
// This method is called from AM with its lock held, so don't take the DPMS lock.
// b/29242568
- ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
- if (profileOwner != null) {
- return DevicePolicyManagerService.this
- .createShowAdminSupportIntent(profileOwner, userId);
- }
-
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null && deviceOwner.first == userId) {
- return DevicePolicyManagerService.this
- .createShowAdminSupportIntent(deviceOwner.second, userId);
- }
-
- // We're not specifying the device admin because there isn't one.
- if (useDefaultIfNoAdmin) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
+ if (getEnforcingAdminAndUserDetailsInternal(userId, null) != null
+ || useDefaultIfNoAdmin) {
+ return DevicePolicyManagerService.this.createShowAdminSupportIntent(userId);
}
return null;
}
@Override
public Intent createUserRestrictionSupportIntent(int userId, String userRestriction) {
- final long ident = mInjector.binderClearCallingIdentity();
- try {
- final List<UserManager.EnforcingUser> sources = mUserManager
- .getUserRestrictionSources(userRestriction, UserHandle.of(userId));
- if (sources == null || sources.isEmpty()) {
- // The restriction is not enforced.
- return null;
- } else if (sources.size() > 1) {
- // In this case, we'll show an admin support dialog that does not
- // specify the admin.
- // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
- // the admin for the calling user.
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- null, userId);
- }
- final UserManager.EnforcingUser enforcingUser = sources.get(0);
- final int sourceType = enforcingUser.getUserRestrictionSource();
- final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
- if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
- // Restriction was enforced by PO
- final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
- enforcingUserId);
- if (profileOwner != null) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- profileOwner, enforcingUserId);
- }
- } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
- // Restriction was enforced by DO
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- deviceOwner.second, deviceOwner.first);
- }
- } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
- /*
- * In this case, the user restriction is enforced by the system.
- * So we won't show an admin support intent, even if it is also
- * enforced by a profile/device owner.
- */
- return null;
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
+ Intent intent = null;
+ if (getEnforcingAdminAndUserDetailsInternal(userId, userRestriction) != null) {
+ intent = DevicePolicyManagerService.this.createShowAdminSupportIntent(userId);
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, userRestriction);
}
- return null;
+ return intent;
}
@Override
@@ -13070,53 +13018,153 @@
}
}
- private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
+ private Intent createShowAdminSupportIntent(int userId) {
// This method is called with AMS lock held, so don't take DPMS lock
final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
return intent;
}
- @Override
- public Intent createAdminSupportIntent(String restriction) {
- Objects.requireNonNull(restriction);
- final CallerIdentity caller = getCallerIdentity();
- Intent intent = null;
- if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ */
+ private Bundle getEnforcingAdminAndUserDetailsInternal(int userId, String restriction) {
+ Bundle result = null;
+ if (restriction == null) {
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ if (profileOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ profileOwner);
+ return result;
+ }
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null && deviceOwner.first == userId) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ deviceOwner.second);
+ return result;
+ }
+ } else if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)
+ || DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
synchronized (getLockObject()) {
- final DevicePolicyData policy = getUserData(caller.getUserId());
+ final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
final ActiveAdmin admin = policy.mAdminList.get(i);
if ((admin.disableCamera &&
- DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
- (admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
- intent = createShowAdminSupportIntent(admin.info.getComponent(),
- caller.getUserId());
- break;
+ DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction))
+ || (admin.disableScreenCapture && DevicePolicyManager
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ admin.info.getComponent());
+ return result;
}
}
// For the camera, a device owner on a different user can disable it globally,
// so we need an additional check.
- if (intent == null
+ if (result == null
&& DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
final ActiveAdmin admin = getDeviceOwnerAdminLocked();
if (admin != null && admin.disableCamera) {
- intent = createShowAdminSupportIntent(admin.info.getComponent(),
- mOwners.getDeviceOwnerUserId());
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, mOwners.getDeviceOwnerUserId());
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ admin.info.getComponent());
+ return result;
}
}
}
} else {
- // if valid, |restriction| can only be a user restriction
- intent = mLocalService.createUserRestrictionSupportIntent(caller.getUserId(),
- restriction);
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ List<UserManager.EnforcingUser> sources = mUserManager
+ .getUserRestrictionSources(restriction, UserHandle.of(userId));
+ if (sources == null || sources.isEmpty()) {
+ // The restriction is not enforced.
+ return null;
+ } else if (sources.size() > 1) {
+ // In this case, we'll show an admin support dialog that does not
+ // specify the admin.
+ // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
+ // the admin for the calling user.
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ return result;
+ }
+ final UserManager.EnforcingUser enforcingUser = sources.get(0);
+ final int sourceType = enforcingUser.getUserRestrictionSource();
+ final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
+ if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
+ // Restriction was enforced by PO
+ final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
+ enforcingUserId);
+ if (profileOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, enforcingUserId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ profileOwner);
+ return result;
+ }
+ } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+ // Restriction was enforced by DO
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, deviceOwner.first);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ deviceOwner.second);
+ return result;
+ }
+ } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
+ /*
+ * In this case, the user restriction is enforced by the system.
+ * So we won't show an admin support intent, even if it is also
+ * enforced by a profile/device owner.
+ */
+ return null;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
- if (intent != null) {
+ return null;
+ }
+
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ * @return Details of admin and user which enforced the restriction for the userId.
+ */
+ @Override
+ public Bundle getEnforcingAdminAndUserDetails(int userId, String restriction) {
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ return getEnforcingAdminAndUserDetailsInternal(userId, restriction);
+ }
+
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ */
+ @Override
+ public Intent createAdminSupportIntent(String restriction) {
+ Objects.requireNonNull(restriction);
+ final CallerIdentity caller = getCallerIdentity();
+ final int userId = caller.getUserId();
+ Intent intent = null;
+ if (getEnforcingAdminAndUserDetailsInternal(userId, restriction) != null) {
+ intent = createShowAdminSupportIntent(userId);
intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
}
return intent;
@@ -14300,6 +14348,7 @@
maybePauseDeviceWideLoggingLocked();
maybeResumeDeviceWideLoggingLocked();
maybeClearLockTaskPolicyLocked();
+ updateAdminCanGrantSensorsPermissionCache(callingUserId);
}
}
@@ -17480,7 +17529,10 @@
});
}
- private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) {
+ private void setAdminCanGrantSensorsPermissionForUserUnchecked(@UserIdInt int userId,
+ boolean canGrant) {
+ Slogf.d(LOG_TAG, "setAdminCanGrantSensorsPermissionForUserUnchecked(%d, %b)",
+ userId, canGrant);
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
@@ -17494,10 +17546,18 @@
}
}
- private void updateAdminCanGrantSensorsPermissionCache(int userId) {
+ private void updateAdminCanGrantSensorsPermissionCache(@UserIdInt int userId) {
synchronized (getLockObject()) {
- ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
- final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+
+ ActiveAdmin owner;
+ // If the user is affiliated the device (either a DO itself, or an affiliated PO),
+ // use mAdminCanGrantSensorsPermissions from the DO
+ if (isUserAffiliatedWithDeviceLocked(userId)) {
+ owner = getDeviceOwnerAdminLocked();
+ } else {
+ owner = getDeviceOrProfileOwnerAdminLocked(userId);
+ }
+ boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
}
}
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index a2768c6..3a9b2dc 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.content.Context;
import android.os.ISystemConfig;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -84,6 +85,21 @@
}
return ArrayUtils.convertToIntArray(uids);
}
+
+ @Override
+ public List<String> getEnabledComponentOverrides(String packageName) {
+ ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
+ .getComponentsEnabledStates(packageName);
+ List<String> enabledComponent = new ArrayList<>();
+ if (systemComponents != null) {
+ for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
+ if (Boolean.TRUE.equals(entry.getValue())) {
+ enabledComponent.add(entry.getKey());
+ }
+ }
+ }
+ return enabledComponent;
+ }
};
public SystemConfigService(Context context) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index b60e0c3..6a4d69f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -87,6 +87,7 @@
@Mock private Context mContext;
@Mock private IStorageManager mStorageManager;
@Mock private ApexManager mApexManager;
+ @Mock private PackageManagerService mMockPackageManagerInternal;
private File mTmpDir;
private StagingManager mStagingManager;
@@ -826,7 +827,7 @@
PackageInstallerSession session = new PackageInstallerSession(
/* callback */ null,
/* context */ null,
- /* pm */ null,
+ /* pm */ mMockPackageManagerInternal,
/* sessionProvider */ null,
/* silentUpdatePolicy */ null,
/* looper */ BackgroundThread.getHandler().getLooper(),
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index ce90d99..e9b5b62 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-
import static org.junit.Assert.assertNull;
import android.content.Intent;
@@ -183,7 +181,6 @@
null /* callerFeatureId */,
0 /* callingPid */,
0 /* callingUid */,
- PROCESS_STATE_NONEXISTENT,
false /* callerInstantApp */,
null /* resolvedType */,
null /* requiredPermissions */,
@@ -201,6 +198,6 @@
userId,
false /* allowBackgroundActivityStarts */,
null /* activityStartsToken */,
- false /* timeoutExempt */);
+ false /* timeoutExempt */ );
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
new file mode 100644
index 0000000..2f45fb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class SensorOverlaysTest {
+
+ private static final int SENSOR_ID = 11;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock private ISidefpsController mSidefpsController;
+ @Mock private AcquisitionClient<?> mAcquisitionClient;
+
+ @Test
+ public void noopWhenBothNull() {
+ final SensorOverlays useless = new SensorOverlays(null, null);
+ useless.show(SENSOR_ID, 2, null);
+ useless.hide(SENSOR_ID);
+ }
+
+ @Test
+ public void testProvidesUdfps() {
+ final List<IUdfpsOverlayController> udfps = new ArrayList<>();
+ SensorOverlays sensorOverlays = new SensorOverlays(null, mSidefpsController);
+
+ sensorOverlays.ifUdfps(udfps::add);
+ assertThat(udfps).isEmpty();
+
+ sensorOverlays = new SensorOverlays(mUdfpsOverlayController, mSidefpsController);
+ sensorOverlays.ifUdfps(udfps::add);
+ assertThat(udfps).containsExactly(mUdfpsOverlayController);
+ }
+
+ @Test
+ public void testShow() throws Exception {
+ testShow(mUdfpsOverlayController, mSidefpsController);
+ }
+
+ @Test
+ public void testShowUdfps() throws Exception {
+ testShow(mUdfpsOverlayController, null);
+ }
+
+ @Test
+ public void testShowSidefps() throws Exception {
+ testShow(null, mSidefpsController);
+ }
+
+ private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps)
+ throws Exception {
+ final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+ final int reason = BiometricOverlayConstants.REASON_UNKNOWN;
+ sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient);
+
+ if (udfps != null) {
+ verify(mUdfpsOverlayController).showUdfpsOverlay(eq(SENSOR_ID), eq(reason), any());
+ }
+ if (sidefps != null) {
+ verify(mSidefpsController).show();
+ }
+ }
+
+ @Test
+ public void testHide() throws Exception {
+ testHide(mUdfpsOverlayController, mSidefpsController);
+ }
+
+ @Test
+ public void testHideUdfps() throws Exception {
+ testHide(mUdfpsOverlayController, null);
+ }
+
+ @Test
+ public void testHideSidefps() throws Exception {
+ testHide(null, mSidefpsController);
+ }
+
+ private void testHide(IUdfpsOverlayController udfps, ISidefpsController sidefps)
+ throws Exception {
+ final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+ sensorOverlays.hide(SENSOR_ID);
+
+ if (udfps != null) {
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(eq(SENSOR_ID));
+ }
+ if (sidefps != null) {
+ verify(mSidefpsController).hide();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index b51918e..8b7c90d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -19,10 +19,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorLocation;
@@ -56,6 +59,8 @@
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
private UserManager mUserManager;
@Mock
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@@ -74,19 +79,21 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class));
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
sensor1.commonProps.sensorId = 0;
- sensor1.sensorLocations = new SensorLocation[] {new SensorLocation()};
+ sensor1.sensorLocations = new SensorLocation[]{new SensorLocation()};
final SensorProps sensor2 = new SensorProps();
sensor2.commonProps = new CommonProps();
sensor2.commonProps.sensorId = 1;
- sensor2.sensorLocations = new SensorLocation[] {new SensorLocation()};
+ sensor2.sensorLocations = new SensorLocation[]{new SensorLocation()};
- mSensorProps = new SensorProps[] {sensor1, sensor2};
+ mSensorProps = new SensorProps[]{sensor1, sensor2};
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7b20bf0..140a8eb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -101,6 +101,7 @@
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Process;
@@ -2096,9 +2097,12 @@
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID, null,
+ Build.VERSION_CODES.Q);
dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
boolean originalCameraDisabled = dpm.getCameraDisabled(admin1);
assertExpectException(SecurityException.class, /* messageRegex= */ null,
() -> dpm.setCameraDisabled(admin1, true));
@@ -2665,8 +2669,8 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
// Test 1. Caller doesn't have DO or DA.
- assertExpectException(SecurityException.class, /* messageRegex= */ "No active admin",
- () -> dpm.getWifiMacAddress(admin1));
+ assertExpectException(SecurityException.class, /* messageRegex= */
+ "does not exist or is not owned by uid", () -> dpm.getWifiMacAddress(admin1));
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
@@ -2960,9 +2964,6 @@
assertThat(intent.getAction()).isEqualTo(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1))
.isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID));
- assertThat(
- (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN))
- .isEqualTo(admin1);
assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION))
.isEqualTo(UserManager.DISALLOW_ADJUST_VOLUME);
@@ -2999,7 +3000,7 @@
assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION))
.isEqualTo(DevicePolicyManager.POLICY_DISABLE_CAMERA);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1))
- .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID));
+ .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_UID));
// ScreenCapture should not be disabled by device owner
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
assertThat(intent).isNull();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 5de0c2b..1973410 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -434,7 +434,8 @@
final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
.addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
- final ScanResult scanResult = mMockPackageManager.scanPackageOnlyLI(
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
+ final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -482,7 +483,8 @@
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
- ScanResult result = mMockPackageManager.scanPackageOnlyLI(
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mMockPackageManager);
+ ScanResult result = scanPackageHelper.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 9ad479a..9a14e7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2724,9 +2724,11 @@
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
task.addChild(taskFragment2, POSITION_TOP);
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE).build();
activity2.mVisibleRequested = true;
taskFragment2.addChild(activity2);
+ assertTrue(activity2.isResizeable());
activity1.reparent(taskFragment1, POSITION_TOP);
assertEquals(task, activity1.mStartingData.mAssociatedTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index b95d56b..764f63d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -43,13 +43,17 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -61,6 +65,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.mockito.MockitoSession;
import java.util.ArrayList;
@@ -80,6 +85,9 @@
private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
ArgumentCaptor.forClass(ClientTransaction.class);
+ private static final String DEFAULT_PACKAGE_NAME = "my.application.package";
+ private static final int DEFAULT_USER_ID = 100;
+
@Before
public void setUp() throws Exception {
setBooted(mAtm);
@@ -301,6 +309,15 @@
// The top app should not change while sleeping.
assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+ mAtm.startLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY
+ | ActivityTaskManagerService.POWER_MODE_REASON_UNKNOWN_VISIBILITY);
+ assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
+ // Because there is no unknown visibility record, the state will be restored if other
+ // reasons are all done.
+ mAtm.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
+ assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
+ mAtm.mInternal.getTopProcessState());
+
// If all activities are stopped, the sleep wake lock must be released.
final Task topRootTask = topActivity.getRootTask();
doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
@@ -480,5 +497,269 @@
assertTrue(activity.supportsMultiWindow());
assertTrue(task.supportsMultiWindow());
}
+
+ @Test
+ public void testPackageConfigUpdate_locales_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")).commit();
+
+ WindowProcessController wpcAfterConfigChange = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_nightMode_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertTrue(wpcAfterConfigChange.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_multipleLocaleUpdates_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpc.getConfiguration().getLocales());
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("ja-XC,en-XC")).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"),
+ wpc.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_multipleNightModeUpdates_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_NO).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_onPackageUninstall_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME);
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_LocalesEmptyAndNightModeUndefined_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpc.getConfiguration().getLocales());
+
+ packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList())
+ .setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpc.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_WhenUserRemoved_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ mAtm.mInternal.removeUser(DEFAULT_USER_ID);
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_setLocaleListToEmpty_doesNotOverlayLocaleListInWpc() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_resetNightMode_doesNotOverrideNightModeInWpc() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ private WindowProcessController createWindowProcessController(String packageName,
+ int userId) {
+ WindowProcessListener mMockListener = Mockito.mock(WindowProcessListener.class);
+ ApplicationInfo info = mock(ApplicationInfo.class);
+ info.packageName = packageName;
+ WindowProcessController wpc = new WindowProcessController(
+ mAtm, info, packageName, 0, userId, null, mMockListener);
+ wpc.setThread(mock(IApplicationThread.class));
+ return wpc;
+ }
+
}
+
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 53bae41..0d0cec7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -103,6 +103,20 @@
}
@Test
+ public void testClearTaskSkipAppExecuteTransition() {
+ final ActivityRecord behind = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final Task task = behind.getTask();
+ final ActivityRecord top = createActivityRecord(task);
+ top.setState(ActivityRecord.State.RESUMED, "test");
+ behind.setState(ActivityRecord.State.STARTED, "test");
+ behind.mVisibleRequested = true;
+
+ task.performClearTask("test");
+ assertFalse(mDisplayContent.mAppTransition.isReady());
+ }
+
+ @Test
public void testTranslucentOpen() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4ee0c60..e340217 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1572,6 +1572,12 @@
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
assertTrue(displayRotation.updateRotationUnchecked(false));
+ // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep).
+ mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
+ displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
+ ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false;
+ assertTrue(displayRotation.updateRotationUnchecked(false));
+
// Rotation can be updated if the recents animation is animating but it is not on top, e.g.
// switching activities in different orientations by quickstep gesture.
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
@@ -2173,6 +2179,21 @@
}
@Test
+ public void testKeyguardGoingAwayWhileAodShown() {
+ mDisplayContent.getDisplayPolicy().setAwake(true);
+
+ final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+ final ActivityRecord activity = appWin.mActivityRecord;
+
+ mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
+ true /* aodShowing */);
+ assertFalse(activity.isVisibleRequested());
+
+ mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
+ assertTrue(activity.isVisibleRequested());
+ }
+
+ @Test
public void testRemoveRootTaskInWindowingModes() {
removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
WINDOWING_MODE_FULLSCREEN));
@@ -2319,11 +2340,10 @@
// mirror.
setUpDefaultTaskDisplayAreaWindowToken();
- // GIVEN SurfaceControl can successfully mirror the provided surface.
+ // GIVEN SurfaceControl does not mirror a null surface.
Point surfaceSize = new Point(
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
- surfaceControlMirrors(surfaceSize);
// GIVEN a new VirtualDisplay with an associated surface.
final VirtualDisplay display = createVirtualDisplay(surfaceSize, null /* surface */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 609d159..78946fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -62,7 +62,8 @@
mSurfaces = new SurfaceControlMocker();
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
() -> mAreCornersRounded, () -> Color.valueOf(mColor),
- () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha);
+ () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
+ /* doubleTapCallback= */ () -> {});
mTransaction = spy(StubTransaction.class);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6407c92..348472a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1171,6 +1171,7 @@
final WindowState w = addWindowToActivity(mActivity);
// Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
mActivity.mRootWindowContainer.performSurfacePlacement();
+ mActivity.layoutLetterbox(null);
// The letterbox insets should be [450, 0 - 250, 0].
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
@@ -1832,6 +1833,9 @@
}
private void assertLetterboxSurfacesDrawnBetweenActivityAndParentBounds(Rect parentBounds) {
+ // Ensure Letterbox is updated.
+ mActivity.layoutLetterbox(null);
+
// Letterbox should fill the gap between the parent bounds and the letterboxed activity.
final Rect letterboxedBounds = new Rect(mActivity.getBounds());
assertTrue(parentBounds.contains(letterboxedBounds));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 9160109..17ae2e8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -797,6 +797,9 @@
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
@Override
+ public void onImeDrawnOnTask(int taskId) throws RemoteException {
+ }
+ @Override
public void onAppSplashScreenViewRemoved(int taskId) {
}
};
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index d3f2d14..c56b614 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -34,6 +34,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -49,6 +50,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -371,8 +373,9 @@
public void testTopActivityUiModeChangeScheduleConfigChange() {
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
- doReturn(true).when(activity).setOverrideNightMode(anyInt());
- mWpc.updateNightModeForAllActivities(Configuration.UI_MODE_NIGHT_YES);
+ doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any());
+ mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES,
+ LocaleList.forLanguageTags("en-XA"));
verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 454ecd7..1f6065f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -223,6 +223,10 @@
// {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
// may be set on some device form factors.
mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
+ // Ensure letterbox reachability treatment isn't overridden on any device target.
+ // {@link com.android.internal.R.bool.config_letterboxIsReachabilityEnabled},
+ // may be set on some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration.setIsReachabilityEnabled(false);
checkDeviceSpecificOverridesNotApplied();
}
@@ -230,12 +234,9 @@
@After
public void tearDown() throws Exception {
// Revert back to device overrides.
- mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
- mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio));
- mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
- mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier));
+ mAtm.mWindowManager.mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mAtm.mWindowManager.mLetterboxConfiguration.resetIsReachabilityEnabled();
}
/**
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 734172f..f05dd63 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -272,13 +272,11 @@
Slog.v(TAG, "cancelLocked");
clearDebugHotwordLoggingTimeoutLocked();
mDebugHotwordLogging = false;
- if (mRemoteHotwordDetectionService.isBound()) {
- mRemoteHotwordDetectionService.unbind();
- LocalServices.getService(PermissionManagerServiceInternal.class)
- .setHotwordDetectionServiceProvider(null);
- mIdentity = null;
- updateServiceUidForAudioPolicy(Process.INVALID_UID);
- }
+ mRemoteHotwordDetectionService.unbind();
+ LocalServices.getService(PermissionManagerServiceInternal.class)
+ .setHotwordDetectionServiceProvider(null);
+ mIdentity = null;
+ updateServiceUidForAudioPolicy(Process.INVALID_UID);
mCancellationTaskFuture.cancel(/* may interrupt */ true);
if (mAudioFlinger != null) {
mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e21d0ad..b2ea79f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14298,7 +14298,8 @@
if (callForwardingInfo.getNumber() == null) {
throw new IllegalArgumentException("callForwarding number is null");
}
- if (callForwardingInfo.getTimeoutSeconds() <= 0) {
+ if (callForwardingReason == CallForwardingInfo.REASON_NO_REPLY
+ && callForwardingInfo.getTimeoutSeconds() <= 0) {
throw new IllegalArgumentException("callForwarding timeout isn't positive");
}
}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 460a26d..0bb6198 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -32,7 +32,9 @@
":android-test-mock-sources",
// Note: Below are NOT APIs of this library. We only take APIs under
// the android.test.mock package. They however provide private APIs that
- // android.test.mock APIs references to.
+ // android.test.mock APIs references to. We need to have the classes in
+ // source code form to have access to the @hide comment which disappears
+ // when the classes are compiled into a Jar library.
":framework-core-sources-for-test-mock",
":framework_native_aidl",
],
@@ -46,6 +48,14 @@
api_packages: [
"android.test.mock",
],
+ // Only include android.test.mock.* classes. Jarjar rules below removes
+ // classes in other packages like android.content. In order to keep the
+ // list up-to-date, permitted_packages ensures that the library contains
+ // clases under android.test.mock after the jarjar rules are applied.
+ jarjar_rules: "jarjar-rules.txt",
+ permitted_packages: [
+ "android.test.mock",
+ ],
compile_dex: true,
default_to_stubs: true,
dist_group: "android",
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
new file mode 100644
index 0000000..4420a44
--- /dev/null
+++ b/test-mock/jarjar-rules.txt
@@ -0,0 +1,7 @@
+zap android.accounts.**
+zap android.app.**
+zap android.content.**
+zap android.database.**
+zap android.os.**
+zap android.util.**
+zap android.view.**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 2cc1943..7981827 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,22 +17,21 @@
@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker
-import android.content.ComponentName
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
-val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+val LAUNCHER_COMPONENT = FlickerComponentName("com.google.android.apps.nexuslauncher",
"com.google.android.apps.nexuslauncher.NexusLauncherActivity")
fun FlickerTestParameter.statusBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.STATUS_BAR)
}
}
fun FlickerTestParameter.navBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -71,19 +70,19 @@
fun FlickerTestParameter.navBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
}
fun FlickerTestParameter.statusBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
}
@@ -96,10 +95,10 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
+ this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
+ this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(endingPos)
}
}
@@ -112,10 +111,10 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
+ this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
+ this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(endingPos)
}
}
@@ -132,15 +131,15 @@
* (useful mostly for app launch)
*/
fun FlickerTestParameter.replacesLayer(
- originalLayer: ComponentName,
- newLayer: ComponentName,
+ originalLayer: FlickerComponentName,
+ newLayer: FlickerComponentName,
ignoreSnapshot: Boolean = false
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
if (ignoreSnapshot) {
assertion.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
}
assertion.then().isVisible(newLayer)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index bd7c185..0b1748a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import androidx.test.uiautomator.UiDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class ImeAppAutoFocusHelper @JvmOverloads constructor(
@@ -27,7 +28,8 @@
private val rotation: Int,
private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(
device: UiDevice,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d224af9..1c2164a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class ImeAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index 3074e28..f7ca5ce 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 02be3cf..7bab981 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index d7cbaae..f6a8817 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 19fefb9..59e8dc8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index d17e77d..3550536 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -37,7 +37,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -125,7 +125,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -133,7 +133,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 6f0f55a..0352185 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -122,7 +122,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -130,7 +130,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
@@ -173,8 +173,8 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 6cdf32c..11660df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -35,8 +35,8 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Assume
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,9 +91,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 8aaf925..bb2ffbc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -37,7 +37,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -95,9 +95,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
@@ -157,8 +157,8 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7659d94..ba78e25 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,52 +18,52 @@
package com.android.server.wm.flicker.ime
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
} else {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index fe1b1cd..d31c9bc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
+import android.os.SystemProperties
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -42,7 +42,8 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,6 +62,8 @@
class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val isShellTransitionsEnabled =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -100,12 +103,12 @@
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- val component = ComponentName("", "RecentTaskScreenshotSurface")
+ val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- component)
+ ignoreWindows = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ component)
)
}
}
@@ -122,15 +125,17 @@
@Presubmit
@Test
- fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(true)
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
@Presubmit
@Test
- fun imeAppWindowVisibility() {
- // the app starts visible in live tile, then becomes invisible during animation and
- // is again launched. Since we log 1x per frame, sometimes the activity visibility and
- // the app visibility are updated together, sometimes not, thus ignore activity check
- // at the start
+ fun imeAppWindowVisibilityLegacy() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ // the app starts visible in live tile, and stays visible for the duration of entering
+ // and exiting overview. However, legacy transitions seem to have a bug which causes
+ // everything to restart during the test, so expect the app to disappear and come back.
+ // Since we log 1x per frame, sometimes the activity visibility and the app visibility
+ // are updated together, sometimes not, thus ignore activity check at the start
testSpec.assertWm {
this.isAppWindowVisible(testApp.component, ignoreActivity = true)
.then()
@@ -142,6 +147,19 @@
@Presubmit
@Test
+ fun imeAppWindowVisibility() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ // the app starts visible in live tile, and stays visible for the duration of entering
+ // and exiting overview. Since we log 1x per frame, sometimes the activity visibility
+ // and the app visibility are updated together, sometimes not, thus ignore activity
+ // check at the start
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ }
+ }
+
+ @Presubmit
+ @Test
// During testing the launcher is always in portrait mode
fun entireScreenCovered() = testSpec.entireScreenCovered()
@@ -155,13 +173,23 @@
@Presubmit
@Test
- fun imeLayerIsBecomesVisible() {
+ fun imeLayerIsBecomesVisibleLegacy() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerIsBecomesVisible() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ testSpec.assertLayers {
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -171,7 +199,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp.component)
}
@@ -194,11 +222,11 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
// sometimes this layer appears for 2-3 frames, sometimes for only 1
- val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
+ val recentTaskComponent = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+ listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, recentTaskComponent)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 1c32e32..8cff595 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -36,7 +35,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -107,7 +106,7 @@
@Test
fun imeAppWindowVisibility() {
- val component = ComponentName(imeTestApp.`package`, "")
+ val component = FlickerComponentName(imeTestApp.`package`, "")
testSpec.assertWm {
this.isAppWindowOnTop(component)
.then()
@@ -118,40 +117,40 @@
@Test
fun navBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
}
@Test
fun statusBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
}
@Test
fun imeLayerIsVisibleWhenSwitchingToImeApp() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersTag(TAG_IME_VISIBLE) {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
}
@Test
fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
- isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 3e35e21..d369de9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -31,6 +31,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,12 +79,16 @@
@Presubmit
@Test
fun finishSubActivity() {
+ val buttonActivityComponent = ActivityOptions
+ .BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ val imeAutoFocusActivityComponent = ActivityOptions
+ .SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
testSpec.assertWm {
- this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
+ this.isAppWindowOnTop(buttonActivityComponent)
+ .then()
+ .isAppWindowOnTop(imeAutoFocusActivityComponent)
+ .then()
+ .isAppWindowOnTop(buttonActivityComponent)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index b717612..b842681 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -29,7 +28,7 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,7 +60,7 @@
@Group1
class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
- private val colorFadComponent = ComponentName("", "ColorFade BLAST#")
+ private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
/**
* Defines the transition used to run the test
@@ -92,15 +91,15 @@
* Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
* and becomes visible at the end
*/
- @Presubmit
+ @Postsubmit
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
.then()
- .isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -159,11 +158,11 @@
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -173,6 +172,10 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
/** {@inheritDoc} */
@FlakyTest
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 14d17f8..e6ce88e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -39,8 +39,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@@ -188,9 +187,9 @@
testSpec.assertWm {
this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowOnTop(testApp.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 035aac1..091180f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -39,7 +39,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -189,7 +189,7 @@
testSpec.assertWm {
this.isAppWindowInvisible(testApp1.component)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component, ignoreActivity = true)
}
@@ -251,7 +251,7 @@
// TODO: Do we actually want to test this? Seems too implementation specific...
.isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component)
}
@@ -270,7 +270,7 @@
.then()
.isVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp1.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index b975360..ed6f5c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -40,7 +40,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -207,7 +207,7 @@
testSpec.assertWm {
this.isAppWindowInvisible(testApp2.component)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp2.component, ignoreActivity = true)
}
@@ -268,7 +268,7 @@
.then()
.isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp2.component)
}
@@ -287,7 +287,7 @@
.then()
.isVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp2.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index b010d4c..edf4408 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -262,7 +262,7 @@
testSpec.assertWm {
this.isAppWindowVisible(LAUNCHER_COMPONENT)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -278,7 +278,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(SNAPSHOT_COMPONENT)
+ .isVisible(FlickerComponentName.SNAPSHOT)
.then()
.isVisible(testApp.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 33ddd00..6f9171b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -30,7 +30,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,7 +72,7 @@
testSpec.assertLayers {
this.isVisible(testApp.component)
.then()
- .isVisible(ROTATION_COMPONENT)
+ .isVisible(FlickerComponentName.ROTATION)
.then()
.isVisible(testApp.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 612ff9d..6966846 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -31,7 +30,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
@@ -86,9 +85,9 @@
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- ComponentName("", "SecondaryHomeHandle")
+ ignoreLayers = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ FlickerComponentName("", "SecondaryHomeHandle")
)
)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 48efe73..aed2ea6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -114,7 +114,7 @@
@Test
fun statusBarWindowIsAlwaysInvisible() {
testSpec.assertWm {
- this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowInvisible(FlickerComponentName.STATUS_BAR)
}
}
@@ -122,7 +122,7 @@
@Test
fun statusBarLayerIsAlwaysInvisible() {
testSpec.assertLayers {
- this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isInvisible(FlickerComponentName.STATUS_BAR)
}
}
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
index 4e2009d..e5eb3c7 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -51,6 +51,7 @@
package_name: "android.content.componentalias.tests",
manifest: "AndroidManifest.xml",
additional_manifests: [
+ "AndroidManifest_main.xml",
"AndroidManifest_service_aliases.xml",
"AndroidManifest_service_targets.xml",
],
@@ -65,7 +66,7 @@
package_name: "android.content.componentalias.tests.sub1",
manifest: "AndroidManifest.xml",
additional_manifests: [
- "AndroidManifest_service_aliases.xml",
+ "AndroidManifest_sub1.xml",
"AndroidManifest_service_targets.xml",
],
test_config_template: "AndroidTest-template.xml",
@@ -79,7 +80,7 @@
package_name: "android.content.componentalias.tests.sub2",
manifest: "AndroidManifest.xml",
additional_manifests: [
- "AndroidManifest_service_aliases.xml",
+ "AndroidManifest_sub2.xml",
"AndroidManifest_service_targets.xml",
],
test_config_template: "AndroidTest-template.xml",
diff --git a/tests/componentalias/AndroidManifest.xml b/tests/componentalias/AndroidManifest.xml
index 893be8e..7bb83a3 100755
--- a/tests/componentalias/AndroidManifest.xml
+++ b/tests/componentalias/AndroidManifest.xml
@@ -20,10 +20,6 @@
<application>
<uses-library android:name="android.test.runner" />
+ <property android:name="com.android.EXPERIMENTAL_COMPONENT_ALIAS_OPT_IN" android:value="true" />
</application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.content.componentalias.tests" >
- </instrumentation>
</manifest>
diff --git a/tests/componentalias/AndroidManifest_main.xml b/tests/componentalias/AndroidManifest_main.xml
new file mode 100755
index 0000000..70e817e
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_main.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests" >
+
+ <application>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.componentalias.tests" >
+ </instrumentation>
+</manifest>
diff --git a/tests/componentalias/AndroidManifest_sub1.xml b/tests/componentalias/AndroidManifest_sub1.xml
new file mode 100755
index 0000000..21616f5
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_sub1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests" >
+
+ <application>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.componentalias.tests.sub1" >
+ </instrumentation>
+</manifest>
diff --git a/tests/componentalias/AndroidManifest_sub2.xml b/tests/componentalias/AndroidManifest_sub2.xml
new file mode 100755
index 0000000..c11b0cd
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_sub2.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests" >
+
+ <application>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.componentalias.tests.sub2" >
+ </instrumentation>
+</manifest>
diff --git a/tests/componentalias/AndroidTest-template.xml b/tests/componentalias/AndroidTest-template.xml
index afdfe79..2d46217 100644
--- a/tests/componentalias/AndroidTest-template.xml
+++ b/tests/componentalias/AndroidTest-template.xml
@@ -21,6 +21,8 @@
<option name="test-file-name" value="ComponentAliasTests2.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android" />
+
<!-- Exempt the helper APKs from the BG restriction, so they can start BG services. -->
<option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests" />
<option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.sub1" />
diff --git a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
index a62d9eb..89db2f7 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
+++ b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
@@ -39,10 +39,8 @@
@Before
public void enableComponentAlias() throws Exception {
sDeviceConfig.set("component_alias_overrides", "");
- sDeviceConfig.set("enable_experimental_component_alias", "true");
- // Device config propagation happens on a handler, so we need to wait for AM to
- // actually set it.
+ // Make sure the feature is actually enabled.
TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
return ShellUtils.runShellCommand("dumpsys activity component-alias")
.indexOf("Enabled: true") > 0;
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
index eab0a6c..a568313 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
@@ -20,11 +20,14 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.hamcrest.core.Is.is;
+
import android.content.ComponentName;
import android.content.Intent;
import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
+import org.junit.Assume;
import org.junit.Test;
import java.util.function.Consumer;
@@ -74,6 +77,8 @@
@Test
public void testBroadcast_explicitPackageName() {
+ // TODO Fix it -- it should work even when called from sub-packages.
+ Assume.assumeThat(sContext.getPackageName(), is(MAIN_PACKAGE));
forEachCombo((c) -> {
Intent i = new Intent().setPackage(c.alias.getPackageName());
i.setAction(c.action);
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
index 35feda1..8bc6f16 100644
--- a/tools/bit/print.cpp
+++ b/tools/bit/print.cpp
@@ -17,6 +17,7 @@
#include "print.h"
#include <sys/ioctl.h>
+#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
diff --git a/tools/bit/util.h b/tools/bit/util.h
index 7ccdab1..8c66911 100644
--- a/tools/bit/util.h
+++ b/tools/bit/util.h
@@ -17,6 +17,8 @@
#ifndef UTIL_H
#define UTIL_H
+#include <sys/types.h>
+
#include <map>
#include <string>
#include <vector>
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
index c133226..930378b 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
@@ -39,6 +39,8 @@
import org.jetbrains.uast.getParentOfType
import org.jetbrains.uast.getQualifiedParentOrThis
import org.jetbrains.uast.getUCallExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
+import org.jetbrains.uast.skipParenthesizedExprUp
/**
* Lint Detector that finds issues with improper usages of the token returned by
@@ -87,7 +89,8 @@
* - Stores token variable name, scope in the file, location and finally block in tokensMap
*/
override fun visitLocalVariable(node: ULocalVariable) {
- val rhsExpression = node.uastInitializer?.getUCallExpression() ?: return
+ val initializer = node.uastInitializer?.skipParenthesizedExprDown()
+ val rhsExpression = initializer?.getUCallExpression() ?: return
if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
val location = context.getLocation(node as UElement)
val variableName = node.getName()
@@ -162,7 +165,8 @@
return
}
if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
- val arg = node.valueArguments[0] as? USimpleNameReferenceExpression ?: return
+ val first = node.valueArguments[0].skipParenthesizedExprDown()
+ val arg = first as? USimpleNameReferenceExpression ?: return
val variableName = arg.identifier
val originalScope = tokensMap[variableName]?.scope ?: return
val psi = arg.sourcePsi ?: return
@@ -177,7 +181,7 @@
// receiver.selector, so to get the call's immediate parent we need to get the topmost
// parent qualified reference expression and access its parent
if (tokensMap[variableName]?.finallyBlock != null &&
- node.getQualifiedParentOrThis().uastParent !=
+ skipParenthesizedExprUp(node.getQualifiedParentOrThis().uastParent) !=
tokensMap[variableName]?.finallyBlock) {
context.report(
ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 0cd9037..6890d99 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -1,5 +1,6 @@
#include "Errors.h"
+#include <stdarg.h>
#include <stdlib.h>
namespace android {