Merge "Fix Multi user avater View overlap privacy dot" into sc-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9d149cf..4b054f4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6122,28 +6122,29 @@
// change the background bgColor
CharSequence title = action.title;
ColorStateList[] outResultColor = new ColorStateList[1];
- int background = getColors(p).getSecondaryAccentColor();
+ int buttonFillColor = getColors(p).getSecondaryAccentColor();
if (isLegacy()) {
title = ContrastColorUtil.clearColorSpans(title);
} else {
- title = ensureColorSpanContrast(title, background, outResultColor);
+ int notifBackgroundColor = getColors(p).getBackgroundColor();
+ title = ensureColorSpanContrast(title, notifBackgroundColor, outResultColor);
}
button.setTextViewText(R.id.action0, processTextSpans(title));
boolean hasColorOverride = outResultColor[0] != null;
if (hasColorOverride) {
// There's a span spanning the full text, let's take it and use it as the
// background color
- background = outResultColor[0].getDefaultColor();
+ buttonFillColor = outResultColor[0].getDefaultColor();
}
final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- background, mInNightMode);
+ buttonFillColor, mInNightMode);
button.setTextColor(R.id.action0, textColor);
// We only want about 20% alpha for the ripple
final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
button.setColorStateList(R.id.action0, "setRippleColor",
ColorStateList.valueOf(rippleColor));
button.setColorStateList(R.id.action0, "setButtonBackground",
- ColorStateList.valueOf(background));
+ ColorStateList.valueOf(buttonFillColor));
if (p.mCallStyleActions) {
button.setImageViewIcon(R.id.action0, action.getIcon());
boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
@@ -6176,8 +6177,8 @@
* there exists a full length color span.
* @return the contrasted charSequence
*/
- private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
- ColorStateList[] outResultColor) {
+ private static CharSequence ensureColorSpanContrast(CharSequence charSequence,
+ int background, ColorStateList[] outResultColor) {
if (charSequence instanceof Spanned) {
Spanned ss = (Spanned) charSequence;
Object[] spans = ss.getSpans(0, ss.length(), Object.class);
@@ -6197,8 +6198,9 @@
int[] colors = textColor.getColors();
int[] newColors = new int[colors.length];
for (int i = 0; i < newColors.length; i++) {
+ boolean isBgDark = isColorDark(background);
newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
- colors[i], background, mInNightMode);
+ colors[i], background, isBgDark);
}
textColor = new ColorStateList(textColor.getStates().clone(),
newColors);
@@ -6217,8 +6219,9 @@
} else if (resultSpan instanceof ForegroundColorSpan) {
ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
int foregroundColor = originalSpan.getForegroundColor();
+ boolean isBgDark = isColorDark(background);
foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
- foregroundColor, background, mInNightMode);
+ foregroundColor, background, isBgDark);
if (fullLength) {
outResultColor[0] = ColorStateList.valueOf(foregroundColor);
resultSpan = null;
@@ -6238,6 +6241,19 @@
}
/**
+ * Determines if the color is light or dark. Specifically, this is using the same metric as
+ * {@link ContrastColorUtil#resolvePrimaryColor(Context, int, boolean)} and peers so that
+ * the direction of color shift is consistent.
+ *
+ * @param color the color to check
+ * @return true if the color has higher contrast with white than black
+ */
+ private static boolean isColorDark(int color) {
+ // as per ContrastColorUtil.shouldUseDark, this uses the color contrast midpoint.
+ return ContrastColorUtil.calculateLuminance(color) <= 0.17912878474;
+ }
+
+ /**
* @return Whether we are currently building a notification from a legacy (an app that
* doesn't create material notifications by itself) app.
*/
diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java
index 5694ca8..266e33a 100644
--- a/core/java/android/appwidget/AppWidgetManagerInternal.java
+++ b/core/java/android/appwidget/AppWidgetManagerInternal.java
@@ -19,6 +19,8 @@
import android.annotation.Nullable;
import android.util.ArraySet;
+import java.util.Set;
+
/**
* App widget manager local system service interface.
*
@@ -42,4 +44,16 @@
* @param userId The user that is being unlocked.
*/
public abstract void unlockUser(int userId);
+
+ /**
+ * Updates all widgets, applying changes to Runtime Resource Overlay affecting the specified
+ * target packages.
+ *
+ * @param packageNames The names of all target packages for which an overlay was modified
+ * @param userId The user for which overlay modifications occurred.
+ * @param updateFrameworkRes Whether or not an overlay affected the values of framework
+ * resources.
+ */
+ public abstract void applyResourceOverlaysToWidgets(Set<String> packageNames, int userId,
+ boolean updateFrameworkRes);
}
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index cb3bf29..8ff0181 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -168,6 +168,15 @@
dest.writeByteArray(mManufacturerDataMask);
}
}
+
+ // IRK
+ if (mDeviceAddress != null) {
+ dest.writeInt(mAddressType);
+ dest.writeInt(mIrk == null ? 0 : 1);
+ if (mIrk != null) {
+ dest.writeByteArray(mIrk);
+ }
+ }
}
/**
@@ -187,8 +196,10 @@
if (in.readInt() == 1) {
builder.setDeviceName(in.readString());
}
+ String address = null;
+ // If we have a non-null address
if (in.readInt() == 1) {
- builder.setDeviceAddress(in.readString());
+ address = in.readString();
}
if (in.readInt() == 1) {
ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
@@ -245,6 +256,17 @@
}
}
+ // IRK
+ if (address != null) {
+ final int addressType = in.readInt();
+ if (in.readInt() == 1) {
+ final byte[] irk = new byte[16];
+ in.readByteArray(irk);
+ builder.setDeviceAddress(address, addressType, irk);
+ } else {
+ builder.setDeviceAddress(address, addressType);
+ }
+ }
return builder.build();
}
};
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4526ab77..fa7ce11 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -24,12 +24,12 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyToggleSourceProto;
import android.util.ArrayMap;
@@ -379,7 +379,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
- return isSensorPrivacyEnabled(sensor, getCurrentUserId());
+ return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT);
}
/**
@@ -410,7 +410,7 @@
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
boolean enable) {
- setSensorPrivacy(source, sensor, enable, getCurrentUserId());
+ setSensorPrivacy(source, sensor, enable, UserHandle.USER_CURRENT);
}
/**
@@ -446,7 +446,7 @@
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
@Sensors.Sensor int sensor, boolean enable) {
- setSensorPrivacyForProfileGroup(source , sensor, enable, getCurrentUserId());
+ setSensorPrivacyForProfileGroup(source , sensor, enable, UserHandle.USER_CURRENT);
}
/**
@@ -481,7 +481,7 @@
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(int sensor,
boolean suppress) {
- suppressSensorPrivacyReminders(sensor, suppress, getCurrentUserId());
+ suppressSensorPrivacyReminders(sensor, suppress, UserHandle.USER_CURRENT);
}
/**
@@ -609,12 +609,4 @@
}
}
- private int getCurrentUserId() {
- try {
- return ActivityManager.getService().getCurrentUserId();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return 0;
- }
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f3e0ce9..1bc6495 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1460,15 +1460,15 @@
/** {@hide} */
@VisibleForTesting
public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
- try {
- Context context = AppGlobals.getInitialApplication();
- if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
- // Never convert modern fd for MediaProvider, because this requires
- // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
- return null;
- }
- return MediaStore.getOriginalMediaFormatFileDescriptor(context,
- ParcelFileDescriptor.dup(fd));
+ Context context = AppGlobals.getInitialApplication();
+ if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
+ // Never convert modern fd for MediaProvider, because this requires
+ // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
+ return null;
+ }
+
+ try (ParcelFileDescriptor dupFd = ParcelFileDescriptor.dup(fd)) {
+ return MediaStore.getOriginalMediaFormatFileDescriptor(context, dupFd);
} catch (Exception e) {
Log.d(TAG, "Failed to convert to modern format file descriptor", e);
return null;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6bca336..9f37c48 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,12 +34,9 @@
import libcore.io.IoUtils;
-import java.io.BufferedReader;
import java.io.FileDescriptor;
-import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
-import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;
/**
@@ -1472,43 +1469,4 @@
}
private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
-
- /**
- * Checks if a process corresponding to a specific pid owns any file locks.
- * @param pid The process ID for which we want to know the existence of file locks.
- * @return true If the process holds any file locks, false otherwise.
- * @throws IOException if /proc/locks can't be accessed.
- *
- * @hide
- */
- public static boolean hasFileLocks(int pid) throws Exception {
- BufferedReader br = null;
-
- try {
- br = new BufferedReader(new FileReader("/proc/locks"));
- String line;
-
- while ((line = br.readLine()) != null) {
- StringTokenizer st = new StringTokenizer(line);
-
- for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
- String str = st.nextToken();
- try {
- if (i == 4 && Integer.parseInt(str) == pid) {
- return true;
- }
- } catch (NumberFormatException nfe) {
- throw new Exception("Exception parsing /proc/locks at \" "
- + line + " \", token #" + i);
- }
- }
- }
-
- return false;
- } finally {
- if (br != null) {
- br.close();
- }
- }
- }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index f21d855dd..6c2d6a1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1242,7 +1242,7 @@
mBlastSurfaceControl.setTransformHint(mTransformHint);
if (mBlastBufferQueue != null) {
mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight,
- mFormat);
+ mFormat, transaction);
}
} else {
transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 38019c9..5f036a3 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1013,8 +1013,10 @@
*
* @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
* hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
- * @return {@code true} on success, {@code false} if the input connection is no longer valid, or
- * the protocol is not supported.
+ * @return For editor authors, the return value will always be ignored. For IME authors, this
+ * method returns {@code true} if the request was sent (whether or not the associated
+ * editor does something based on this request), {@code false} if the input connection
+ * is no longer valid.
*/
default boolean setImeConsumesInput(boolean imeConsumesInput) {
return false;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e827f0a..91fc5a5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5824,6 +5824,25 @@
return false;
}
+ /** @hide */
+ public void updateAppInfo(@NonNull ApplicationInfo info) {
+ if (mApplication != null && mApplication.sourceDir.equals(info.sourceDir)) {
+ // Overlay paths are generated against a particular version of an application.
+ // The overlays paths of a newly upgraded application are incompatible with the
+ // old version of the application.
+ mApplication = info;
+ }
+ if (hasSizedRemoteViews()) {
+ for (RemoteViews layout : mSizedRemoteViews) {
+ layout.updateAppInfo(info);
+ }
+ }
+ if (hasLandscapeAndPortraitLayouts()) {
+ mLandscape.updateAppInfo(info);
+ mPortrait.updateAppInfo(info);
+ }
+ }
+
private Context getContextForResources(Context context) {
if (mApplication != null) {
if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
diff --git a/core/java/com/android/internal/os/ProcLocksReader.java b/core/java/com/android/internal/os/ProcLocksReader.java
new file mode 100644
index 0000000..bd3115fc5
--- /dev/null
+++ b/core/java/com/android/internal/os/ProcLocksReader.java
@@ -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.internal.os;
+
+import com.android.internal.util.ProcFileReader;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Reads and parses {@code locks} files in the {@code proc} filesystem.
+ * A typical example of /proc/locks
+ *
+ * 1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335
+ * 2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF
+ * 2: -> POSIX ADVISORY WRITE 18291 fd:09:34062 0 EOF
+ * 2: -> POSIX ADVISORY WRITE 18293 fd:09:34062 0 EOF
+ * 3: POSIX ADVISORY READ 3888 fd:09:13992 128 128
+ * 4: POSIX ADVISORY READ 3888 fd:09:14230 1073741826 1073742335
+ */
+public class ProcLocksReader {
+ private final String mPath;
+
+ public ProcLocksReader() {
+ mPath = "/proc/locks";
+ }
+
+ public ProcLocksReader(String path) {
+ mPath = path;
+ }
+
+ /**
+ * Checks if a process corresponding to a specific pid owns any file locks.
+ * @param pid The process ID for which we want to know the existence of file locks.
+ * @return true If the process holds any file locks, false otherwise.
+ * @throws IOException if /proc/locks can't be accessed.
+ */
+ public boolean hasFileLocks(int pid) throws Exception {
+ ProcFileReader reader = null;
+ long last = -1;
+ long id; // ordinal position of the lock in the list
+ int owner; // the PID of the process that owns the lock
+
+ try {
+ reader = new ProcFileReader(new FileInputStream(mPath));
+
+ while (reader.hasMoreData()) {
+ id = reader.nextLong(true); // lock id
+ if (id == last) {
+ reader.finishLine(); // blocked lock
+ continue;
+ }
+
+ reader.nextIgnored(); // lock type: POSIX?
+ reader.nextIgnored(); // lock type: MANDATORY?
+ reader.nextIgnored(); // lock type: RW?
+
+ owner = reader.nextInt(); // pid
+ if (owner == pid) {
+ return true;
+ }
+ reader.finishLine();
+ last = id;
+ }
+ } catch (IOException e) {
+ // TODO: let ProcFileReader log the failed line
+ throw new Exception("Exception parsing /proc/locks");
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+ return false;
+ }
+}
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
index ead58c7d..0dd8ad8 100644
--- a/core/java/com/android/internal/util/ProcFileReader.java
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -28,8 +28,8 @@
* requires each line boundary to be explicitly acknowledged using
* {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.
* <p>
- * Currently doesn't support formats based on {@code \0}, tabs, or repeated
- * delimiters.
+ * Currently doesn't support formats based on {@code \0}, tabs.
+ * Consecutive spaces are treated as a single delimiter.
*/
public class ProcFileReader implements Closeable {
private final InputStream mStream;
@@ -75,6 +75,11 @@
private void consumeBuf(int count) throws IOException {
// TODO: consider moving to read pointer, but for now traceview says
// these copies aren't a bottleneck.
+
+ // skip all consecutive delimiters.
+ while (count < mTail && mBuffer[count] == ' ') {
+ count++;
+ }
System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
mTail -= count;
if (mTail == 0) {
@@ -159,11 +164,18 @@
* Parse and return next token as base-10 encoded {@code long}.
*/
public long nextLong() throws IOException {
+ return nextLong(false);
+ }
+
+ /**
+ * Parse and return next token as base-10 encoded {@code long}.
+ */
+ public long nextLong(boolean stopAtInvalid) throws IOException {
final int tokenIndex = nextTokenIndex();
if (tokenIndex == -1) {
throw new ProtocolException("Missing required long");
} else {
- return parseAndConsumeLong(tokenIndex);
+ return parseAndConsumeLong(tokenIndex, stopAtInvalid);
}
}
@@ -176,7 +188,7 @@
if (tokenIndex == -1) {
return def;
} else {
- return parseAndConsumeLong(tokenIndex);
+ return parseAndConsumeLong(tokenIndex, false);
}
}
@@ -186,7 +198,10 @@
return s;
}
- private long parseAndConsumeLong(int tokenIndex) throws IOException {
+ /**
+ * If stopAtInvalid is true, don't throw IOException but return whatever parsed so far.
+ */
+ private long parseAndConsumeLong(int tokenIndex, boolean stopAtInvalid) throws IOException {
final boolean negative = mBuffer[0] == '-';
// TODO: refactor into something like IntegralToString
@@ -194,7 +209,11 @@
for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
final int digit = mBuffer[i] - '0';
if (digit < 0 || digit > 9) {
- throw invalidLong(tokenIndex);
+ if (stopAtInvalid) {
+ break;
+ } else {
+ throw invalidLong(tokenIndex);
+ }
}
// always parse as negative number and apply sign later; this
@@ -226,6 +245,18 @@
return (int) value;
}
+ /**
+ * Bypass the next token.
+ */
+ public void nextIgnored() throws IOException {
+ final int tokenIndex = nextTokenIndex();
+ if (tokenIndex == -1) {
+ throw new ProtocolException("Missing required token");
+ } else {
+ consumeBuf(tokenIndex + 1);
+ }
+ }
+
@Override
public void close() throws IOException {
mStream.close();
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b46b5a2..d4ae6d7 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -105,9 +105,11 @@
}
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
- jlong height, jint format) {
+ jlong height, jint format, jlong transactionPtr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
- queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+ queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format,
+ transaction);
}
static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
@@ -144,7 +146,7 @@
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
- {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
+ {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
{"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeSetTransactionCompleteCallback",
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf2f677..0310b18 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2079,7 +2079,7 @@
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string>
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string>
- <string name="call_notification_screening_text" msgid="8396931408268940208">"رصد مكالمة واردة"</string>
+ <string name="call_notification_screening_text" msgid="8396931408268940208">"يتم فحص المكالمة الواردة"</string>
<plurals name="selected_count" formatted="false" msgid="3946212171128200491">
<item quantity="zero">تم اختيار <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر</item>
<item quantity="two">تم اختيار عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 542387f..199e82f 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -984,7 +984,7 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nMöchtest du diese Seite wirklich verlassen?"</string>
<string name="save_password_label" msgid="9161712335355510035">"Bestätigen"</string>
<string name="double_tap_toast" msgid="7065519579174882778">"Tipp: Zum Vergrößern und Verkleinern doppeltippen"</string>
- <string name="autofill_this_form" msgid="3187132440451621492">"AutoFill"</string>
+ <string name="autofill_this_form" msgid="3187132440451621492">"Automatisches Ausfüllen"</string>
<string name="setup_autofill" msgid="5431369130866618567">"Autom.Ausfüll.konf."</string>
<string name="autofill_window_title" msgid="4379134104008111961">"Mit <xliff:g id="SERVICENAME">%1$s</xliff:g> automatisch ausfüllen"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9a07025..9a29add 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -271,7 +271,7 @@
<string name="global_action_settings" msgid="4671878836947494217">"Ajustes"</string>
<string name="global_action_assist" msgid="2517047220311505805">"Asistencia"</string>
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
- <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo seguro"</string>
+ <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo de seguridad"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"> 999"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
<string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"Teclado virtual"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 816ddd4..2e4578c 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -29,5 +29,8 @@
<color name="resolver_empty_state_text">#FFFFFF</color>
<color name="resolver_empty_state_icon">#FFFFFF</color>
+ <color name="call_notification_decline_color">#E66A5E</color>
+ <color name="call_notification_answer_color">#5DBA80</color>
+
<color name="personal_apps_suspension_notification_color">#8AB4F8</color>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3f9cee1..2936a08 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1949,7 +1949,7 @@
<string name="maximize_button_text" msgid="4258922519914732645">"بڑا کریں"</string>
<string name="close_button_text" msgid="10603510034455258">"بند کریں"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
- <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب دیں"</string>
<string name="call_notification_answer_video_action" msgid="2086030940195382249">"ویڈیو"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string>
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
new file mode 100644
index 0000000..d800c2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcLocksReaderTest {
+ private File mProcDirectory;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mProcDirectory);
+ }
+
+ @Test
+ public void testRunSimpleLocks() throws Exception {
+ String simpleLocks =
+ "1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335\n" +
+ "2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF\n";
+ assertFalse(runHasFileLocks(simpleLocks, 18402));
+ assertFalse(runHasFileLocks(simpleLocks, 18404));
+ assertTrue(runHasFileLocks(simpleLocks, 18403));
+ assertTrue(runHasFileLocks(simpleLocks, 18292));
+ }
+
+ @Test
+ public void testRunBlockedLocks() throws Exception {
+ String blockedLocks =
+ "1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335\n" +
+ "2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF\n" +
+ "2: -> POSIX ADVISORY WRITE 18291 fd:09:34062 0 EOF\n" +
+ "2: -> POSIX ADVISORY WRITE 18293 fd:09:34062 0 EOF\n" +
+ "3: POSIX ADVISORY READ 3888 fd:09:13992 128 128\n" +
+ "4: POSIX ADVISORY READ 3888 fd:09:14230 1073741826 1073742335\n";
+ assertFalse(runHasFileLocks(blockedLocks, 18402));
+ assertFalse(runHasFileLocks(blockedLocks, 18404));
+ assertTrue(runHasFileLocks(blockedLocks, 18403));
+ assertTrue(runHasFileLocks(blockedLocks, 18292));
+
+ assertFalse(runHasFileLocks(blockedLocks, 18291));
+ assertFalse(runHasFileLocks(blockedLocks, 18293));
+ assertTrue(runHasFileLocks(blockedLocks, 3888));
+ }
+
+ private boolean runHasFileLocks(String fileContents, int pid) throws Exception {
+ File tempFile = File.createTempFile("locks", null, mProcDirectory);
+ Files.write(tempFile.toPath(), fileContents.getBytes());
+ boolean result = new ProcLocksReader(tempFile.toString()).hasFileLocks(pid);
+ Files.delete(tempFile.toPath());
+ return result;
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
index b6da195..0532628 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -166,6 +166,46 @@
assertEquals(-1L, reader.nextOptionalLong(-1L));
}
+ public void testInvalidLongs() throws Exception {
+ final ProcFileReader reader = buildReader("12: 34\n56 78@#\n");
+
+ assertEquals(12L, reader.nextLong(true));
+ assertEquals(34L, reader.nextLong(true));
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals(56L, reader.nextLong(true));
+ assertEquals(78L, reader.nextLong(true));
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testConsecutiveDelimiters() throws Exception {
+ final ProcFileReader reader = buildReader("1 2 3 4 5\n");
+
+ assertEquals(1L, reader.nextLong());
+ assertEquals(2L, reader.nextLong());
+ assertEquals(3L, reader.nextLong());
+ assertEquals(4L, reader.nextLong());
+ assertEquals(5L, reader.nextLong());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testIgnore() throws Exception {
+ final ProcFileReader reader = buildReader("a b c\n");
+
+ assertEquals("a", reader.nextString());
+ assertTrue(reader.hasMoreData());
+
+ reader.nextIgnored();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals("c", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
private static ProcFileReader buildReader(String string) throws IOException {
return buildReader(string, 2048);
}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 6c1c2ee..36215ec 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -33,7 +33,7 @@
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
- int format);
+ int format, long transactionPtr);
private static native void nativeFlushShadowQueue(long ptr);
private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
long frameNumber);
@@ -92,9 +92,15 @@
* @param width The new width for the buffer.
* @param height The new height for the buffer.
* @param format The new format for the buffer.
+ * @param t Adds destination frame changes to the passed in transaction.
*/
+ public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format,
+ SurfaceControl.Transaction t) {
+ nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, t.mNativeObject);
+ }
+
public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {
- nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);
+ nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, 0);
}
/**
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 74fb618..4925209 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -53,6 +53,7 @@
private long mStartTime;
private boolean mForceSoftware;
private Animator mLoopAnimation;
+ private Animator mCurrentAnimation;
RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
boolean forceSoftware) {
@@ -74,6 +75,12 @@
return this;
}
+ void end() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.end();
+ }
+ }
+
@NonNull RippleAnimationSession exit(Canvas canvas) {
if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
else exitSoftware();
@@ -114,10 +121,12 @@
if (mLoopAnimation != null) mLoopAnimation.cancel();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+ if (mCurrentAnimation == expand) mCurrentAnimation = null;
}
});
expand.setInterpolator(LINEAR_INTERPOLATOR);
expand.start();
+ mCurrentAnimation = expand;
}
private long computeDelay() {
@@ -147,6 +156,7 @@
if (mLoopAnimation != null) mLoopAnimation.cancel();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+ if (mCurrentAnimation == exit) mCurrentAnimation = null;
}
});
exit.setTarget(canvas);
@@ -155,6 +165,7 @@
long delay = computeDelay();
exit.setStartDelay(delay);
exit.start();
+ mCurrentAnimation = exit;
}
private void enterHardware(RecordingCanvas canvas) {
@@ -167,6 +178,7 @@
mStartTime + MAX_NOISE_PHASE);
loop.setTarget(canvas);
startAnimation(expand, loop);
+ mCurrentAnimation = expand;
}
private void startAnimation(Animator expand, Animator loop) {
@@ -200,6 +212,7 @@
mProperties.getShader().setNoisePhase((float) loop.getAnimatedValue());
});
startAnimation(expand, loop);
+ mCurrentAnimation = expand;
}
void setRadius(float radius) {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b994ad2..d3cff5c 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -278,6 +278,15 @@
}
cancelExitingRipples();
+ endPatternedAnimations();
+ }
+
+ private void endPatternedAnimations() {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RippleAnimationSession session = mRunningAnimations.get(i);
+ session.end();
+ }
+ mRunningAnimations.clear();
}
private void cancelExitingRipples() {
@@ -291,7 +300,6 @@
Arrays.fill(ripples, 0, count, null);
}
mExitingRipplesCount = 0;
- mExitingAnimation = true;
// Always draw an additional "clean" frame after canceling animations.
invalidateSelf(false);
}
@@ -714,7 +722,7 @@
}
cancelExitingRipples();
- exitPatternedAnimation();
+ endPatternedAnimations();
}
@Override
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d746c85..37054b8 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1573,6 +1573,9 @@
if (isFdDuped) {
closeFileDescriptor(fileDescriptor);
}
+ if (modernFd != null) {
+ modernFd.close();
+ }
}
}
@@ -2554,12 +2557,13 @@
private void initForFilename(String filename) throws IOException {
FileInputStream in = null;
+ ParcelFileDescriptor modernFd = null;
mAssetInputStream = null;
mFilename = filename;
mIsInputStream = false;
try {
in = new FileInputStream(filename);
- ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(in.getFD());
+ modernFd = FileUtils.convertToModernFd(in.getFD());
if (modernFd != null) {
closeQuietly(in);
in = new FileInputStream(modernFd.getFileDescriptor());
@@ -2570,6 +2574,9 @@
loadAttributes(in);
} finally {
closeQuietly(in);
+ if (modernFd != null) {
+ modernFd.close();
+ }
}
}
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 2943eee..a15529e 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -36,6 +36,7 @@
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.text.TextUtils;
+import android.util.Log;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -52,6 +53,8 @@
* frame and meta data from an input media file.
*/
public class MediaMetadataRetriever implements AutoCloseable {
+ private static final String TAG = "MediaMetadataRetriever";
+
// borrowed from ExoPlayer
private static final String[] STANDARD_GENRES = new String[] {
// These are the official ID3v1 genres.
@@ -301,11 +304,15 @@
*/
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IllegalArgumentException {
- ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
- if (modernFd == null) {
- _setDataSource(fd, offset, length);
- } else {
- _setDataSource(modernFd.getFileDescriptor(), offset, length);
+
+ try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+ if (modernFd == null) {
+ _setDataSource(fd, offset, length);
+ } else {
+ _setDataSource(modernFd.getFileDescriptor(), offset, length);
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Ignoring IO error while setting data source", e);
}
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba..26eb2a9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1271,11 +1271,14 @@
*/
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
- ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
- if (modernFd == null) {
- _setDataSource(fd, offset, length);
- } else {
- _setDataSource(modernFd.getFileDescriptor(), offset, length);
+ try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+ if (modernFd == null) {
+ _setDataSource(fd, offset, length);
+ } else {
+ _setDataSource(modernFd.getFileDescriptor(), offset, length);
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Ignoring IO error while setting data source", e);
}
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 628f7ee..7f7fb60 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -292,8 +292,7 @@
}
synchronized (mRoutesLock) {
for (MediaRoute2Info route : mRoutes.values()) {
- if (sessionInfo.getSelectedRoutes().contains(route.getId())
- || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
routes.add(route);
continue;
}
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b3bdf89..65992a7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -444,7 +444,7 @@
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Il telefono potrebbe spegnersi a breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Il tablet potrebbe spegnersi a breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Il dispositivo potrebbe spegnersi a breve"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index b1aee50..7387985 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -309,7 +309,7 @@
<string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string>
- <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрсөтүлөт"</string>
+ <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрүнөт"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Алыскы түзмөктөр өтө катуу добуш чыгарып же көзөмөлдөнбөй жатса Bluetooth \"Үндүн абсолюттук деңгээли\" функциясын өчүрөт."</string>
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche функциясынын топтомун иштетет."</string>
<string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Жакшыртылган туташуу функциясын иштетет."</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fea9601..2bdd617 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -443,8 +443,8 @@
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria może się wyczerpać do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Wkrótce telefon może się wyłączyć"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet może się wkrótce wyłączyć"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Urządzenie może się wkrótce wyłączyć"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 1472980..e0339da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -64,9 +64,9 @@
return (dialog, which) -> {
Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
final Intent intent = new Intent(ACTION_LEARN_MORE)
- .setComponent(enforcedAdmin.component)
.putExtra(EXTRA_SETTING_KEY, EXTRA_SETTING_VALUE)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setPackage(enforcedAdmin.component.getPackageName());
context.startActivity(intent);
};
}
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml
index 2b0ab6f..558ec08 100644
--- a/packages/SystemUI/res/drawable/fingerprint_bg.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml
@@ -14,10 +14,11 @@
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="oval">
<solid
- android:color="?android:attr/colorBackground"/>
+ android:color="?androidprv:attr/colorSurface"/>
<size
android:width="64dp"
diff --git a/packages/SystemUI/res/drawable/ic_unlock.xml b/packages/SystemUI/res/drawable/ic_unlock.xml
new file mode 100644
index 0000000..c3b3469
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_unlock.xml
@@ -0,0 +1,42 @@
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:translateX="8.625" android:translateY="13.625">
+ <path
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:pathData="M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
+ </group>
+ <group android:translateX="14" android:translateY="13.5">
+ <path
+ android:strokeColor="#FF000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:pathData="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ <group android:translateX="20" android:translateY="35.75">
+ <path
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 1a91202..b0f1f48 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,9 +55,23 @@
android:id="@+id/lock_icon_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="48px"
- android:layout_gravity="center"
- android:scaleType="centerCrop"/>
+ android:layout_gravity="center">
+ <!-- Background protection -->
+ <ImageView
+ android:id="@+id/lock_icon_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/fingerprint_bg"
+ android:visibility="invisible"/>
+
+ <ImageView
+ android:id="@+id/lock_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="48px"
+ android:layout_gravity="center"
+ android:scaleType="centerCrop"/>
+ </com.android.keyguard.LockIconView>
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
index 8834ac0..18517906 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -26,8 +26,7 @@
android:id="@+id/udfps_keyguard_fp_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/fingerprint_bg"
- android:visibility="gone"/>
+ android:src="@drawable/fingerprint_bg"/>
<!-- Fingerprint -->
<!-- AOD dashed fingerprint icon with moving dashes -->
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7d00c5b..e17f032e 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -799,7 +799,7 @@
<item quantity="other">%d分</item>
<item quantity="one">%d分</item>
</plurals>
- <string name="battery_panel_title" msgid="5931157246673665963">"電池の使用状況"</string>
+ <string name="battery_panel_title" msgid="5931157246673665963">"バッテリーの使用状況"</string>
<string name="battery_detail_charging_summary" msgid="8821202155297559706">"充電中はバッテリー セーバーは利用できません"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"バッテリー セーバー"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"パフォーマンスとバックグラウンド データを制限します"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index f62d9da..8a26ed3 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -707,11 +707,11 @@
<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">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрсөтүлөт \n- Билдирмелер толук экранда көрсөтүлөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрсөтүлөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
+ <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрүнөт \n- Билдирмелер толук экранда көрүнөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрүнөт \n\n"<b>"0-деңгээл"</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>
- <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрсөтүлөт"</string>
+ <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрүнөт"</string>
<string name="notification_channel_unsilenced" msgid="94878840742161152">"Бул билдирмелер тууралуу кабарлап турабыз"</string>
<string name="inline_blocking_helper" msgid="2891486013649543452">"Адатта мындай билдирмелерди өткөрүп жибересиз. \nАлар көрүнө берсинби?"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index c503ebd..40ceb59 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -452,8 +452,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
<string name="tap_again" msgid="1315420114387908655">"फेरि ट्याप गर्नुहोस्"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
- <!-- no translation found for keyguard_unlock_press (8488350566398524740) -->
- <skip />
+ <string name="keyguard_unlock_press" msgid="8488350566398524740">"अनलक गर्न प्रेस गर्नुहोस्"</string>
<string name="keyguard_retry" msgid="886802522584053523">"फेरि प्रयास गर्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC प्रयोग गर्न स्क्रिन अनलक गर्नुहोस्"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"यो डिभाइस तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 6845b5b..a948fae 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -452,8 +452,7 @@
<string name="notification_tap_again" msgid="4477318164947497249">"Trokit përsëri për ta hapur"</string>
<string name="tap_again" msgid="1315420114387908655">"Trokit sërish"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
- <!-- no translation found for keyguard_unlock_press (8488350566398524740) -->
- <skip />
+ <string name="keyguard_unlock_press" msgid="8488350566398524740">"Shtyp për të hapur"</string>
<string name="keyguard_retry" msgid="886802522584053523">"Rrëshqit lart për të provuar përsëri"</string>
<string name="require_unlock_for_nfc" msgid="1305686454823018831">"Shkyçe për të përdorur NFC-në"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 90c1e40..3a3d308 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -209,6 +209,11 @@
@Override
public void onCancelled(
@Nullable WindowInsetsAnimationController controller) {
+ // It is possible to be denied control of ime insets, which means onReady
+ // is never called. We still need to notify the runnables in order to
+ // complete the bouncer disappearing
+ runOnFinishImeAnimationRunnable();
+ finishRunnable.run();
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index c1d448d..622419a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -17,15 +17,21 @@
package com.android.keyguard;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
+import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -33,16 +39,47 @@
/**
* A view positioned under the notification shade.
*/
-public class LockIconView extends ImageView implements Dumpable {
+public class LockIconView extends FrameLayout implements Dumpable {
@NonNull private final RectF mSensorRect;
@NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
private int mRadius;
+ private ImageView mLockIcon;
+ private ImageView mUnlockBgView;
+
+ private int mLockIconColor;
+
public LockIconView(Context context, AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
}
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ mLockIcon = findViewById(R.id.lock_icon);
+ mUnlockBgView = findViewById(R.id.lock_icon_bg);
+ }
+
+ void updateColorAndBackgroundVisibility(boolean useBackground) {
+ if (useBackground) {
+ mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.textColorPrimary);
+ mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+ mUnlockBgView.setVisibility(View.VISIBLE);
+ } else {
+ mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+ R.attr.wallpaperTextColorAccent);
+ mUnlockBgView.setVisibility(View.GONE);
+ }
+
+ mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
+ }
+
+ void setImageDrawable(Drawable drawable) {
+ mLockIcon.setImageDrawable(drawable);
+ }
+
void setCenterLocation(@NonNull PointF center, int radius) {
mLockIconCenter = center;
mRadius = radius;
@@ -56,9 +93,12 @@
setX(mSensorRect.left);
setY(mSensorRect.top);
- setLayoutParams(new FrameLayout.LayoutParams(
+
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
(int) (mSensorRect.right - mSensorRect.left),
- (int) (mSensorRect.bottom - mSensorRect.top)));
+ (int) (mSensorRect.bottom - mSensorRect.top));
+ lp.gravity = Gravity.CENTER;
+ setLayoutParams(lp);
}
@Override
@@ -70,7 +110,6 @@
return mLockIconCenter.y - mRadius;
}
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9c8582fa..4317e25 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -43,7 +43,6 @@
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
@@ -97,8 +96,8 @@
@NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
@NonNull private final Drawable mLockIcon;
@NonNull private final Drawable mUnlockIcon;
- @NonNull private final CharSequence mUnlockedLabel;
- @NonNull private final CharSequence mLockedLabel;
+ @NonNull private CharSequence mUnlockedLabel;
+ @NonNull private CharSequence mLockedLabel;
@Nullable private final Vibrator mVibrator;
private boolean mIsDozing;
@@ -111,7 +110,7 @@
private boolean mUserUnlockedWithBiometric;
private Runnable mCancelDelayedUpdateVisibilityRunnable;
- private boolean mHasUdfps;
+ private boolean mUdfpsSupported;
private float mHeightPixels;
private float mWidthPixels;
private int mBottomPadding; // in pixels
@@ -152,9 +151,8 @@
final Context context = view.getContext();
mUnlockIcon = mView.getContext().getResources().getDrawable(
- R.anim.lock_to_unlock,
+ R.drawable.ic_unlock,
mView.getContext().getTheme());
- ((AnimatedVectorDrawable) mUnlockIcon).start();
mLockIcon = mView.getContext().getResources().getDrawable(
R.anim.lock_to_unlock,
mView.getContext().getTheme());
@@ -177,7 +175,7 @@
protected void onViewAttached() {
// we check this here instead of onInit since the FingerprintManager + FaceManager may not
// have started up yet onInit
- mHasUdfps = mAuthController.getUdfpsSensorLocation() != null;
+ mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
updateConfiguration();
updateKeyguardShowing();
@@ -307,12 +305,7 @@
}
private void updateColors() {
- final int color = Utils.getColorAttrDefaultColor(mView.getContext(),
- R.attr.wallpaperTextColorAccent);
- mFpToUnlockIcon.setTint(color);
- mLockToUnlockIcon.setTint(color);
- mLockIcon.setTint(color);
- mUnlockIcon.setTint(color);
+ mView.updateColorAndBackgroundVisibility(mUdfpsSupported);
}
private void updateConfiguration() {
@@ -321,11 +314,17 @@
mHeightPixels = metrics.heightPixels;
mBottomPadding = mView.getContext().getResources().getDimensionPixelSize(
R.dimen.lock_icon_margin_bottom);
+
+ mUnlockedLabel = mView.getContext().getResources().getString(
+ R.string.accessibility_unlock_button);
+ mLockedLabel = mView.getContext()
+ .getResources().getString(R.string.accessibility_lock_icon);
+
updateLockIconLocation();
}
private void updateLockIconLocation() {
- if (mHasUdfps) {
+ if (mUdfpsSupported) {
FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
props.sensorRadius);
@@ -467,6 +466,7 @@
@Override
public void onConfigChanged(Configuration newConfig) {
updateConfiguration();
+ updateColors();
}
};
@@ -560,7 +560,7 @@
}
private boolean isClickable() {
- return mUdfpsEnrolled || mShowUnlockIcon;
+ return mUdfpsSupported || mShowUnlockIcon;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
index 205054d..98ad875 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
@@ -93,6 +93,8 @@
@Nullable private ModalityListener mModalityListener;
@Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
@Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
+ @Nullable @VisibleForTesting UdfpsIconController mUdfpsIconController;
+
public AuthBiometricFaceToFingerprintView(Context context) {
super(context);
@@ -107,6 +109,12 @@
super(context, attrs, injector);
}
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mUdfpsIconController = new UdfpsIconController(mContext, mIconView, mIndicatorView);
+ }
+
@Modality
int getActiveSensorType() {
return mActiveSensorType;
@@ -168,35 +176,26 @@
}
@Override
- @NonNull
- protected IconController getIconController() {
- if (mActiveSensorType == TYPE_FINGERPRINT) {
- if (!(mIconController instanceof UdfpsIconController)) {
- mIconController = createUdfpsIconController();
- }
- return mIconController;
- }
- return super.getIconController();
- }
-
- @NonNull
- protected IconController createUdfpsIconController() {
- return new UdfpsIconController(getContext(), mIconView, mIndicatorView);
- }
-
- @Override
public void updateState(@BiometricState int newState) {
- if (mState == STATE_HELP || mState == STATE_ERROR) {
- @Modality final int currentType = mActiveSensorType;
- mActiveSensorType = TYPE_FINGERPRINT;
+ if (mActiveSensorType == TYPE_FACE) {
+ if (newState == STATE_HELP || newState == STATE_ERROR) {
+ mActiveSensorType = TYPE_FINGERPRINT;
- setRequireConfirmation(false);
- mConfirmButton.setEnabled(false);
- mConfirmButton.setVisibility(View.GONE);
+ setRequireConfirmation(false);
+ mConfirmButton.setEnabled(false);
+ mConfirmButton.setVisibility(View.GONE);
- if (mModalityListener != null && currentType != mActiveSensorType) {
- mModalityListener.onModalitySwitched(currentType, mActiveSensorType);
+ if (mModalityListener != null) {
+ mModalityListener.onModalitySwitched(TYPE_FACE, mActiveSensorType);
+ }
+
+ // Deactivate the face icon controller so it stops drawing to the view
+ mFaceIconController.deactivate();
+ // Then, activate this icon controller. We need to start in the "error" state
+ mUdfpsIconController.updateState(mState, newState);
}
+ } else { // Fingerprint
+ mUdfpsIconController.updateState(mState, newState);
}
super.updateState(newState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index e8da7c5..c32c1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -49,6 +49,7 @@
protected Handler mHandler;
protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
protected @BiometricState int mState;
+ protected boolean mDeactivated;
protected IconController(Context context, ImageView iconView, TextView textView) {
mContext = context;
@@ -67,6 +68,11 @@
}
protected void animateIcon(int iconRes, boolean repeat) {
+ Log.d(TAG, "animateIcon, state: " + mState + ", deactivated: " + mDeactivated);
+ if (mDeactivated) {
+ return;
+ }
+
final AnimatedVectorDrawable icon =
(AnimatedVectorDrawable) mContext.getDrawable(iconRes);
mIconView.setImageDrawable(icon);
@@ -92,12 +98,26 @@
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
+ Log.d(TAG, "onAnimationEnd, mState: " + mState + ", deactivated: " + mDeactivated);
+ if (mDeactivated) {
+ return;
+ }
+
if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
pulseInNextDirection();
}
}
+ protected void deactivate() {
+ mDeactivated = true;
+ }
+
protected void updateState(int lastState, int newState) {
+ if (mDeactivated) {
+ Log.w(TAG, "Ignoring updateState when deactivated: " + newState);
+ return;
+ }
+
final boolean lastStateIsErrorIcon =
lastState == STATE_ERROR || lastState == STATE_HELP;
@@ -142,7 +162,7 @@
}
}
- protected IconController mIconController;
+ @Nullable @VisibleForTesting IconController mFaceIconController;
public AuthBiometricFaceView(Context context) {
this(context, null);
@@ -158,6 +178,12 @@
}
@Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
+ }
+
+ @Override
protected int getDelayAfterAuthenticatedDurationMs() {
return HIDE_DELAY_MS;
}
@@ -187,17 +213,9 @@
return true;
}
- @NonNull
- protected IconController getIconController() {
- if (mIconController == null) {
- mIconController = new IconController(mContext, mIconView, mIndicatorView);
- }
- return mIconController;
- }
-
@Override
public void updateState(@BiometricState int newState) {
- getIconController().updateState(mState, newState);
+ mFaceIconController.updateState(mState, newState);
if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
(newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 80352d4..e612fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -72,6 +72,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.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
@@ -123,6 +124,7 @@
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Nullable private final UdfpsHbmProvider mHbmProvider;
@NonNull private final KeyguardBypassController mKeyguardBypassController;
+ @NonNull private final ConfigurationController mConfigurationController;
@VisibleForTesting @NonNull final BiometricOrientationEventListener 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.
@@ -522,7 +524,8 @@
@NonNull KeyguardStateController keyguardStateController,
@NonNull KeyguardBypassController keyguardBypassController,
@NonNull DisplayManager displayManager,
- @Main Handler mainHandler) {
+ @Main Handler mainHandler,
+ @NonNull ConfigurationController configurationController) {
mContext = context;
mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
@@ -557,6 +560,7 @@
displayManager,
mainHandler);
mKeyguardBypassController = keyguardBypassController;
+ mConfigurationController = configurationController;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -776,6 +780,7 @@
mDumpManager,
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
+ mConfigurationController,
this
);
case IUdfpsOverlayController.REASON_AUTH_BP:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index eb02aa0..d122610 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -22,7 +22,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -51,17 +50,14 @@
private UdfpsDrawable mFingerprintDrawable; // placeholder
private LottieAnimationView mAodFp;
private LottieAnimationView mLockScreenFp;
- private int mUdfpsBouncerColor;
- private int mWallpaperTextColor;
private int mStatusBarState;
// used when highlighting fp icon:
private int mTextColorPrimary;
private ImageView mBgProtection;
boolean mUdfpsRequested;
- int mUdfpsRequestedColor;
- private AnimatorSet mAnimatorSet;
+ private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
private int mAlpha; // 0-255
// AOD anti-burn-in offsets
@@ -89,20 +85,15 @@
super.onFinishInflate();
mAodFp = findViewById(R.id.udfps_aod_fp);
mLockScreenFp = findViewById(R.id.udfps_lockscreen_fp);
-
mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
- mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
- R.attr.wallpaperTextColorAccent);
- mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
- android.R.attr.textColorPrimary);
+ updateColor();
- // requires call to invalidate to update the color (see #updateColor)
+ // requires call to invalidate to update the color
mLockScreenFp.addValueCallback(
new KeyPath("**"), LottieProperty.COLOR_FILTER,
- frameInfo -> new PorterDuffColorFilter(getColor(), PorterDuff.Mode.SRC_ATOP)
+ frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP)
);
- mUdfpsRequested = false;
mHintAnimator = ObjectAnimator.ofFloat(mLockScreenFp, "progress", 1f, 0f, 1f);
mHintAnimator.setDuration(4000);
@@ -148,13 +139,7 @@
}
void requestUdfps(boolean request, int color) {
- if (request) {
- mUdfpsRequestedColor = color;
- } else {
- mUdfpsRequestedColor = -1;
- }
mUdfpsRequested = request;
- updateColor();
}
void setStatusBarState(int statusBarState) {
@@ -162,31 +147,10 @@
}
void updateColor() {
- mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
- R.attr.wallpaperTextColorAccent);
mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorPrimary);
- mLockScreenFp.invalidate();
- mBgProtection.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
- }
-
- private boolean showingUdfpsBouncer() {
- return mBgProtection.getVisibility() == View.VISIBLE;
- }
-
-
- private int getColor() {
- if (isUdfpsColorRequested()) {
- return mUdfpsRequestedColor;
- } else if (showingUdfpsBouncer()) {
- return mUdfpsBouncerColor;
- } else {
- return mWallpaperTextColor;
- }
- }
-
- private boolean isUdfpsColorRequested() {
- return mUdfpsRequested && mUdfpsRequestedColor != -1;
+ mBgProtection.setImageDrawable(getContext().getDrawable(R.drawable.fingerprint_bg));
+ mLockScreenFp.invalidate(); // updated with a valueCallback
}
/**
@@ -200,7 +164,13 @@
@Override
protected int updateAlpha() {
int alpha = super.updateAlpha();
- mLockScreenFp.setImageAlpha(alpha);
+ mLockScreenFp.setAlpha(alpha / 255f);
+ if (mInterpolatedDarkAmount != 0f) {
+ mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
+ } else {
+ mBgProtection.setAlpha(alpha / 255f);
+ }
+
return alpha;
}
@@ -215,6 +185,7 @@
void onDozeAmountChanged(float linear, float eased) {
mHintAnimator.cancel();
mInterpolatedDarkAmount = eased;
+ updateAlpha();
updateBurnInOffsets();
}
@@ -228,52 +199,21 @@
/**
* Animates in the bg protection circle behind the fp icon to highlight the icon.
*/
- void animateUdfpsBouncer(Runnable onEndAnimation) {
- if (showingUdfpsBouncer() && mBgProtection.getAlpha() == 1f) {
- // already fully highlighted, don't re-animate
+ void animateInUdfpsBouncer(Runnable onEndAnimation) {
+ if (mBackgroundInAnimator.isRunning()) {
+ // already animating in
return;
}
- if (mAnimatorSet != null) {
- mAnimatorSet.cancel();
- }
-
- mAnimatorSet = new AnimatorSet();
- mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mAnimatorSet.setDuration(500);
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBgProtection.setVisibility(View.VISIBLE);
- }
- });
-
- ValueAnimator fpIconColorAnim;
- if (isShadeLocked()) {
- // set color and fade in since we weren't showing before
- mUdfpsBouncerColor = mTextColorPrimary;
- fpIconColorAnim = ValueAnimator.ofInt(0, 255);
- fpIconColorAnim.addUpdateListener(valueAnimator ->
- mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
- } else {
- // update icon color
- fpIconColorAnim = new ValueAnimator();
- fpIconColorAnim.setIntValues(
- isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor,
- mTextColorPrimary);
- fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
- fpIconColorAnim.addUpdateListener(valueAnimator -> {
- mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
- updateColor();
- });
- }
-
- mAnimatorSet.playTogether(
+ // fade in and scale up
+ mBackgroundInAnimator = new AnimatorSet();
+ mBackgroundInAnimator.playTogether(
ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
- ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f),
- fpIconColorAnim);
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
+ mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mBackgroundInAnimator.setDuration(500);
+ mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (onEndAnimation != null) {
@@ -281,66 +221,7 @@
}
}
});
- mAnimatorSet.start();
- }
-
- /**
- * Animates out the bg protection circle behind the fp icon to unhighlight the icon.
- */
- void animateAwayUdfpsBouncer(@Nullable Runnable onEndAnimation) {
- if (!showingUdfpsBouncer()) {
- // already hidden
- return;
- }
-
- if (mAnimatorSet != null) {
- mAnimatorSet.cancel();
- }
-
- ValueAnimator fpIconColorAnim;
- if (isShadeLocked()) {
- // fade out
- mUdfpsBouncerColor = mTextColorPrimary;
- fpIconColorAnim = ValueAnimator.ofInt(255, 0);
- fpIconColorAnim.addUpdateListener(valueAnimator ->
- mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
- } else {
- // update icon color
- fpIconColorAnim = new ValueAnimator();
- fpIconColorAnim.setIntValues(
- mTextColorPrimary,
- isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor);
- fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
- fpIconColorAnim.addUpdateListener(valueAnimator -> {
- mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
- updateColor();
- });
- }
-
- mAnimatorSet = new AnimatorSet();
- mAnimatorSet.playTogether(
- ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f),
- ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f),
- ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f),
- fpIconColorAnim);
- mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mAnimatorSet.setDuration(500);
-
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBgProtection.setVisibility(View.GONE);
- if (onEndAnimation != null) {
- onEndAnimation.run();
- }
- }
- });
-
- mAnimatorSet.start();
- }
-
- boolean isAnimating() {
- return mAnimatorSet != null && mAnimatorSet.isRunning();
+ mBackgroundInAnimator.start();
}
private boolean isShadeLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 6435bdd..58f1254 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -19,6 +19,7 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import android.annotation.NonNull;
+import android.content.res.Configuration;
import android.hardware.biometrics.BiometricSourceType;
import android.util.MathUtils;
import android.view.MotionEvent;
@@ -36,6 +37,7 @@
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.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.FileDescriptor;
@@ -57,6 +59,7 @@
@NonNull private final DelayableExecutor mExecutor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
+ @NonNull private final ConfigurationController mConfigurationController;
@NonNull private final UdfpsController mUdfpsController;
@Nullable private Runnable mCancelDelayedHintRunnable;
@@ -87,6 +90,7 @@
@NonNull DumpManager dumpManager,
@NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
+ @NonNull ConfigurationController configurationController,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBar, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -94,6 +98,7 @@
mExecutor = mainDelayableExecutor;
mKeyguardViewMediator = keyguardViewMediator;
mLockScreenShadeTransitionController = transitionController;
+ mConfigurationController = configurationController;
mUdfpsController = udfpsController;
}
@@ -120,6 +125,7 @@
mQsExpanded = mKeyguardViewManager.isQsExpanded();
mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+ mConfigurationController.addCallback(mConfigurationListener);
updateAlpha();
updatePauseAuth();
@@ -136,6 +142,7 @@
mStatusBarStateController.removeCallback(mStateListener);
mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+ mConfigurationController.removeCallback(mConfigurationListener);
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
@@ -158,7 +165,6 @@
pw.println("mAlpha=" + mView.getAlpha());
pw.println("mUdfpsRequested=" + mUdfpsRequested);
pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
- pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
}
/**
@@ -173,12 +179,17 @@
mShowingUdfpsBouncer = show;
updatePauseAuth();
if (mShowingUdfpsBouncer) {
- mView.animateUdfpsBouncer(() ->
- mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true));
+ if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
+ mView.animateInUdfpsBouncer(null);
+ }
+
+ if (mKeyguardViewManager.isOccluded()) {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+ }
+
mView.announceForAccessibility(mView.getContext().getString(
R.string.accessibility_fingerprint_bouncer));
} else {
- mView.animateAwayUdfpsBouncer(null);
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
}
return true;
@@ -366,7 +377,7 @@
@Override
public boolean isAnimating() {
- return mView.isAnimating();
+ return false;
}
@Override
@@ -407,4 +418,27 @@
pw.println(getTag());
}
};
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onUiModeChanged() {
+ mView.updateColor();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ mView.updateColor();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ mView.updateColor();
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mView.updateColor();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 239a77e..9cb9a36 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -39,6 +39,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
@@ -80,6 +81,7 @@
*/
@Module(includes = {
MediaModule.class,
+ PowerModule.class,
QSModule.class
})
public abstract class SystemUIDefaultModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 556f956..46ab5f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -41,7 +41,6 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
@@ -102,7 +101,6 @@
FalsingModule.class,
LogModule.class,
PeopleHubModule.class,
- PowerModule.class,
PluginModule.class,
ScreenshotModule.class,
SensorModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 425bdc2..1b4ee73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -27,12 +27,15 @@
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.RemeasuringLinearLayout;
import com.android.systemui.R;
@@ -386,19 +389,18 @@
if (mediaView != null) {
index = indexOfChild(mediaView);
}
+ if (mSecurityFooter.getParent() == this && indexOfChild(mSecurityFooter) < index) {
+ // When we remove the securityFooter to rearrange, the index of media will go
+ // down by one, so we correct it
+ index--;
+ }
switchToParent(mSecurityFooter, this, index);
}
}
}
private void switchToParent(View child, ViewGroup parent, int index) {
- ViewGroup currentParent = (ViewGroup) child.getParent();
- if (currentParent != parent || currentParent.indexOfChild(child) != index) {
- if (currentParent != null) {
- currentParent.removeView(child);
- }
- parent.addView(child, index);
- }
+ switchToParent(child, parent, index, getDumpableTag());
}
/** Call when orientation has changed and MediaHost needs to be adjusted. */
@@ -767,4 +769,29 @@
interface OnConfigurationChangedListener {
void onConfigurationChange(Configuration newConfig);
}
+
+ @VisibleForTesting
+ static void switchToParent(View child, ViewGroup parent, int index, String tag) {
+ if (parent == null) {
+ Log.w(tag, "Trying to move view to null parent",
+ new IllegalStateException());
+ return;
+ }
+ ViewGroup currentParent = (ViewGroup) child.getParent();
+ if (currentParent != parent) {
+ if (currentParent != null) {
+ currentParent.removeView(child);
+ }
+ parent.addView(child, index);
+ return;
+ }
+ // Same parent, we are just changing indices
+ int currentIndex = parent.indexOfChild(child);
+ if (currentIndex == index) {
+ // We want to be in the same place. Nothing to do here
+ return;
+ }
+ parent.removeView(child);
+ parent.addView(child, index);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index ce6e469..93e5021 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -45,9 +45,12 @@
import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Background;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.Executor;
+
import javax.inject.Inject;
/**
@@ -63,6 +66,8 @@
private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
+ private final Executor mBgExecutor;
+
/**
* Represents the connection to a target window and provides a mechanism for requesting tiles.
*/
@@ -155,8 +160,10 @@
private IBinder mHostWindowToken;
@Inject
- public ScrollCaptureClient(@UiContext Context context, IWindowManager windowManagerService) {
+ public ScrollCaptureClient(IWindowManager windowManagerService,
+ @Background Executor bgExecutor, @UiContext Context context) {
requireNonNull(context.getDisplay(), "context must be associated with a Display!");
+ mBgExecutor = bgExecutor;
mWindowManagerService = windowManagerService;
}
@@ -220,21 +227,25 @@
return "";
}
SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
- response.getBoundsInWindow(), maxPages);
+ response.getBoundsInWindow(), maxPages, mBgExecutor);
session.start(completer);
return "IScrollCaptureCallbacks#onCaptureStarted";
});
}
private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
- IBinder.DeathRecipient {
+ IBinder.DeathRecipient, ImageReader.OnImageAvailableListener {
private IScrollCaptureConnection mConnection;
+ private final Executor mBgExecutor;
+ private final Object mLock = new Object();
private ImageReader mReader;
private final int mTileHeight;
private final int mTileWidth;
private Rect mRequestRect;
+ private Rect mCapturedArea;
+ private Image mCapturedImage;
private boolean mStarted;
private final int mTargetHeight;
@@ -247,7 +258,8 @@
private Completer<Void> mEndCompleter;
private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
- Rect boundsInWindow, float maxPages) throws RemoteException {
+ Rect boundsInWindow, float maxPages, Executor bgExecutor)
+ throws RemoteException {
mConnection = requireNonNull(connection);
mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
mWindowBounds = requireNonNull(windowBounds);
@@ -259,7 +271,7 @@
mTileWidth = mBoundsInWindow.width();
mTileHeight = pxPerTile / mBoundsInWindow.width();
mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
-
+ mBgExecutor = bgExecutor;
if (DEBUG_SCROLL) {
Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
@@ -289,6 +301,7 @@
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
mStartCompleter = completer;
+ mReader.setOnImageAvailableListenerWithExecutor(this, mBgExecutor);
try {
mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
completer.addCancellationListener(() -> {
@@ -339,9 +352,34 @@
@BinderThread
@Override
- public void onImageRequestCompleted(int flags, Rect contentArea) {
- Image image = mReader.acquireLatestImage();
- mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
+ public void onImageRequestCompleted(int flagsUnused, Rect contentArea) {
+ synchronized (mLock) {
+ mCapturedArea = contentArea;
+ if (mCapturedImage != null || (mCapturedArea == null || mCapturedArea.isEmpty())) {
+ completeCaptureRequest();
+ }
+ }
+ }
+
+ /** @see ImageReader.OnImageAvailableListener */
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ synchronized (mLock) {
+ mCapturedImage = mReader.acquireLatestImage();
+ if (mCapturedArea != null) {
+ completeCaptureRequest();
+ }
+ }
+ }
+
+ /** Produces a result for the caller as soon as both asynchronous results are received. */
+ private void completeCaptureRequest() {
+ CaptureResult result =
+ new CaptureResult(mCapturedImage, mRequestRect, mCapturedArea);
+ mCapturedImage = null;
+ mRequestRect = null;
+ mCapturedArea = null;
+ mTileRequestCompleter.set(result);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 92922b6..3890c1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -821,6 +821,16 @@
}
}
+ private void showTryFingerprintMsg() {
+ if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+ // if udfps available, there will always be a tappable affordance to unlock
+ // For example, the lock icon
+ showTransientIndication(R.string.keyguard_unlock_press);
+ } else {
+ showTransientIndication(R.string.keyguard_try_fingerprint);
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardIndicationController:");
pw.println(" mInitialTextColorState: " + mInitialTextColorState);
@@ -891,7 +901,7 @@
return;
}
- boolean showSwipeToUnlock =
+ boolean showActionToUnlock =
msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
@@ -899,13 +909,11 @@
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
- // suggest trying fingerprint
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showTryFingerprintMsg();
return;
}
- showTransientIndication(helpString, false /* isError */, showSwipeToUnlock);
- }
- if (showSwipeToUnlock) {
+ showTransientIndication(helpString, false /* isError */, showActionToUnlock);
+ } else if (showActionToUnlock) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
}
@@ -921,8 +929,7 @@
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()
&& !mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isScreenOn()) {
- // suggest trying fingerprint
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showTryFingerprintMsg();
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -931,11 +938,10 @@
if (!mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isUdfpsEnrolled()
&& mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- // suggest trying fingerprint
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showTryFingerprintMsg();
} else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
mStatusBarKeyguardViewManager.showBouncerMessage(
- mContext.getResources().getString(R.string.keyguard_try_fingerprint),
+ mContext.getResources().getString(R.string.keyguard_unlock_press),
mInitialTextColorState
);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5e105bb..246810a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -296,8 +296,7 @@
if (mKeyguardPreferredRefreshRate > 0) {
boolean onKeyguard = state.mStatusBarState == StatusBarState.KEYGUARD
- && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway
- && !state.mDozing;
+ && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
if (onKeyguard
&& mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index df889f2..3525100 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -39,6 +39,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
@@ -81,6 +82,7 @@
* overridden by the System UI implementation.
*/
@Module(includes = {
+ PowerModule.class,
QSModule.class
},
subcomponents = {
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 5cd7810..090cf9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -50,7 +49,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -78,14 +76,16 @@
@Mock private TextView mIndicatorView;
@Mock private ImageView mIconView;
@Mock private View mIconHolderView;
- @Mock private AuthBiometricFaceView.IconController mIconController;
+ @Mock private AuthBiometricFaceView.IconController mFaceIconController;
+ @Mock private AuthBiometricFaceToFingerprintView.UdfpsIconController mUdfpsIconController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mFaceToFpView = new TestableView(mContext);
- mFaceToFpView.mIconController = mIconController;
+ mFaceToFpView.mFaceIconController = mFaceIconController;
+ mFaceToFpView.mUdfpsIconController = mUdfpsIconController;
mFaceToFpView.setCallback(mCallback);
mFaceToFpView.mNegativeButton = mNegativeButton;
@@ -99,20 +99,23 @@
@Test
public void testStateUpdated_whenDialogAnimatedIn() {
mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mIconController)
+ verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+ verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
}
@Test
public void testIconUpdatesState_whenDialogStateUpdated() {
mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mIconController)
+ verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+ verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
- verify(mFaceToFpView.mIconController).updateState(
+ verify(mFaceToFpView.mFaceIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
+ verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
}
@@ -120,21 +123,22 @@
@Test
public void testStateUpdated_whenSwitchToFingerprint() {
mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mIconController)
+ verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
- mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
- InOrder order = inOrder(mFaceToFpView.mIconController);
- order.verify(mFaceToFpView.mIconController).updateState(
+ verify(mFaceToFpView.mFaceIconController).deactivate();
+ verify(mFaceToFpView.mUdfpsIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceToFingerprintView.STATE_ERROR));
- order.verify(mFaceToFpView.mIconController).updateState(
+ verify(mConfirmButton).setVisibility(eq(View.GONE));
+
+ mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
+
+ verify(mFaceToFpView.mUdfpsIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_ERROR),
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
-
- verify(mConfirmButton).setVisibility(eq(View.GONE));
}
@Test
@@ -172,7 +176,10 @@
eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
verify(mCallback).onAction(
eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
- assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+ // First we enter the error state, since we need to show the error animation/text. The
+ // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+ assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
}
@Test
@@ -185,13 +192,16 @@
eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
verify(mCallback).onAction(
eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
- assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+ // First we enter the error state, since we need to show the error animation/text. The
+ // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+ assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
}
@Test
public void testFingerprintOnlyStartsOnFirstError() {
mFaceToFpView.onDialogAnimatedIn();
- verify(mFaceToFpView.mIconController)
+ verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
mFaceToFpView.onDialogAnimatedIn();
@@ -260,11 +270,6 @@
protected int getDelayAfterAuthenticatedDurationMs() {
return 0;
}
-
- @Override
- protected IconController createUdfpsIconController() {
- return AuthBiometricFaceToFingerprintViewTest.this.mIconController;
- }
}
private class MockInjector extends AuthBiometricView.Injector {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
index 043bd5c..b93381d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
@@ -62,7 +62,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mFaceView = new TestableFaceView(mContext);
- mFaceView.mIconController = mock(TestableFaceView.TestableIconController.class);
+ mFaceView.mFaceIconController = mock(TestableFaceView.TestableIconController.class);
mFaceView.setCallback(mCallback);
mFaceView.mNegativeButton = mNegativeButton;
@@ -78,18 +78,18 @@
@Test
public void testStateUpdated_whenDialogAnimatedIn() {
mFaceView.onDialogAnimatedIn();
- verify(mFaceView.mIconController)
+ verify(mFaceView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
}
@Test
public void testIconUpdatesState_whenDialogStateUpdated() {
mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING);
- verify(mFaceView.mIconController)
+ verify(mFaceView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
- verify(mFaceView.mIconController).updateState(
+ verify(mFaceView.mFaceIconController).updateState(
eq(AuthBiometricFaceView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceView.STATE_AUTHENTICATED));
}
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 e56b619..2120b0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -63,6 +63,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.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -144,6 +145,8 @@
private DisplayManager mDisplayManager;
@Mock
private Handler mHandler;
+ @Mock
+ private ConfigurationController mConfigurationController;
private FakeExecutor mFgExecutor;
@@ -225,7 +228,8 @@
mKeyguardStateController,
mKeyguardBypassController,
mDisplayManager,
- mHandler);
+ mHandler,
+ mConfigurationController);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
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 f62587c..0c03a51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -39,6 +39,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.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import org.junit.Before;
@@ -76,6 +77,8 @@
@Mock
private KeyguardViewMediator mKeyguardViewMediator;
@Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
private UdfpsController mUdfpsController;
private UdfpsKeyguardViewController mController;
@@ -110,6 +113,7 @@
mDumpManager,
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
+ mConfigurationController,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
new file mode 100644
index 0000000..56f2905
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
@@ -0,0 +1,162 @@
+/*
+ * 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.qs
+
+import com.google.common.truth.Truth.assertThat
+
+import androidx.test.filters.SmallTest
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.children
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSPanelSwitchToParentTest : SysuiTestCase() {
+
+ private lateinit var parent1: FrameLayout
+ private lateinit var parent2: FrameLayout
+
+ private lateinit var movingView: View
+
+ private lateinit var view1A: View
+ private lateinit var view1B: View
+ private lateinit var view1C: View
+
+ private lateinit var view2A: View
+ private lateinit var view2B: View
+ private lateinit var view2C: View
+
+ @Before
+ fun setUp() {
+ parent1 = FrameLayout(mContext)
+ parent2 = FrameLayout(mContext)
+
+ movingView = View(mContext)
+
+ view1A = View(mContext)
+ parent1.addView(view1A)
+ view1B = View(mContext)
+ parent1.addView(view1B)
+ view1C = View(mContext)
+ parent1.addView(view1C)
+
+ view2A = View(mContext)
+ parent2.addView(view2A)
+ view2B = View(mContext)
+ parent2.addView(view2B)
+ view2C = View(mContext)
+ parent2.addView(view2C)
+ }
+
+ @Test
+ fun testNullTargetNoInteractions() {
+ QSPanel.switchToParent(movingView, null, -1, "")
+
+ assertThat(movingView.parent).isNull()
+ }
+
+ @Test
+ fun testMoveToEndNoParent() {
+ QSPanel.switchToParent(movingView, parent2, -1, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, view2B, view2C, movingView
+ )
+ }
+
+ @Test
+ fun testMoveToEndDifferentParent() {
+ parent1.addView(movingView, 0)
+
+ QSPanel.switchToParent(movingView, parent2, -1, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, view2B, view2C, movingView
+ )
+ }
+
+ @Test
+ fun testMoveToEndSameParent() {
+ parent2.addView(movingView, 0)
+
+ QSPanel.switchToParent(movingView, parent2, -1, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, view2B, view2C, movingView
+ )
+ }
+
+ @Test
+ fun testMoveToMiddleFromNoParent() {
+ QSPanel.switchToParent(movingView, parent2, 1, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, movingView, view2B, view2C
+ )
+ }
+
+ @Test
+ fun testMoveToMiddleDifferentParent() {
+ parent1.addView(movingView, 1)
+
+ QSPanel.switchToParent(movingView, parent2, 2, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, view2B, movingView, view2C
+ )
+ }
+
+ @Test
+ fun testMoveToMiddleSameParent() {
+ parent2.addView(movingView, 0)
+
+ QSPanel.switchToParent(movingView, parent2, 1, "")
+
+ assertThat(parent1.childrenList).containsExactly(
+ view1A, view1B, view1C
+ )
+ assertThat(parent2.childrenList).containsExactly(
+ view2A, movingView, view2B, view2C
+ )
+ }
+
+ private val ViewGroup.childrenList: List<View>
+ get() = children.toList()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 4f88599..16d4ddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs;
+import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -21,19 +23,20 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
import org.junit.Test;
@@ -56,29 +59,28 @@
private QSTileImpl dndTile;
@Mock
private QSPanelControllerBase.TileRecord mDndTileRecord;
- @Mock
- private QSLogger mQSLogger;
private ViewGroup mParentView;
@Mock
private QSDetail.Callback mCallback;
@Mock
private QSTileView mQSTileView;
+
+ private UniqueObjectHostView mMediaView;
+ private View mSecurityFooter;
@Mock
- private ActivityStarter mActivityStarter;
+ private FrameLayout mHeaderContainer;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
-// // Dependencies for QSSecurityFooter
-// mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
-// mDependency.injectMockDependency(SecurityController.class);
-// mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
-// mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class));
mDndTileRecord.tile = dndTile;
mDndTileRecord.tileView = mQSTileView;
+ mMediaView = new UniqueObjectHostView(mContext);
+ mSecurityFooter = new View(mContext);
+
mTestableLooper.runWithLooper(() -> {
mQsPanel = new QSPanel(mContext, null);
mQsPanel.initialize();
@@ -92,6 +94,7 @@
when(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView);
mQsPanel.addTile(mDndTileRecord);
mQsPanel.setCallback(mCallback);
+ mQsPanel.setHeaderContainer(mHeaderContainer);
});
}
@@ -112,4 +115,62 @@
verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
}
+
+ @Test
+ public void testSecurityFooterAtEndNoMedia_portrait() {
+ mTestableLooper.processAllMessages();
+
+ mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+ mQsPanel.setSecurityFooter(mSecurityFooter);
+
+ int children = mQsPanel.getChildCount();
+ assertEquals(children - 1, mQsPanel.indexOfChild(mSecurityFooter));
+ }
+
+ @Test
+ public void testSecurityFooterRightBeforeMedia_portrait() {
+ mTestableLooper.processAllMessages();
+
+ mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+ mQsPanel.addView(mMediaView);
+
+ mQsPanel.setSecurityFooter(mSecurityFooter);
+
+ int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
+ int mediaIndex = mQsPanel.indexOfChild(mMediaView);
+
+ assertEquals(mediaIndex - 1, securityFooterIndex);
+ }
+
+ @Test
+ public void testSecurityFooterRightBeforeMedia_portrait_configChange() {
+ mTestableLooper.processAllMessages();
+
+ mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+
+ mQsPanel.addView(mMediaView);
+
+ mQsPanel.setSecurityFooter(mSecurityFooter);
+
+ mQsPanel.onConfigurationChanged(mContext.getResources().getConfiguration());
+
+ int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
+ int mediaIndex = mQsPanel.indexOfChild(mMediaView);
+
+ assertEquals(mediaIndex - 1, securityFooterIndex);
+ }
+
+ @Test
+ public void testSecurityFooterInHeader_landscape() {
+ mTestableLooper.processAllMessages();
+
+ mContext.getResources().getConfiguration().orientation =
+ Configuration.ORIENTATION_LANDSCAPE;
+
+ mQsPanel.setSecurityFooter(mSecurityFooter);
+
+ verify(mHeaderContainer).addView(mSecurityFooter, 0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 3a4bc69..670a130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -36,7 +35,6 @@
import android.view.ScrollCaptureResponse;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
@@ -83,7 +81,7 @@
/* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));
// Create client
- ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
+ ScrollCaptureClient client = new ScrollCaptureClient(mWm, Runnable::run, mContext);
// Request scroll capture
ListenableFuture<ScrollCaptureResponse> requestFuture =
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5aec6aa..a56b1db 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3285,6 +3285,57 @@
}
}
+ private void applyResourceOverlaysToWidgetsLocked(Set<String> packageNames, int userId,
+ boolean updateFrameworkRes) {
+ for (int i = 0, N = mProviders.size(); i < N; i++) {
+ Provider provider = mProviders.get(i);
+ if (provider.getUserId() != userId) {
+ continue;
+ }
+
+ final String packageName = provider.id.componentName.getPackageName();
+ if (!updateFrameworkRes && !packageNames.contains(packageName)) {
+ continue;
+ }
+
+ ApplicationInfo newAppInfo = null;
+ try {
+ newAppInfo = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES, userId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to retrieve app info for " + packageName
+ + " userId=" + userId, e);
+ }
+ if (newAppInfo == null) {
+ continue;
+ }
+ ApplicationInfo oldAppInfo = provider.info.providerInfo.applicationInfo;
+ if (!newAppInfo.sourceDir.equals(oldAppInfo.sourceDir)) {
+ // Overlay paths are generated against a particular version of an application.
+ // The overlays paths of a newly upgraded application are incompatible with the
+ // old version of the application.
+ continue;
+ }
+
+ // Isolate the changes relating to RROs. The app info must be copied to prevent
+ // affecting other parts of system server that may have cached this app info.
+ oldAppInfo = new ApplicationInfo(oldAppInfo);
+ oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone();
+ oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone();
+ provider.info.providerInfo.applicationInfo = oldAppInfo;
+
+ for (int j = 0, M = provider.widgets.size(); j < M; j++) {
+ Widget widget = provider.widgets.get(j);
+ if (widget.views != null) {
+ widget.views.updateAppInfo(oldAppInfo);
+ }
+ if (widget.maskedViews != null) {
+ widget.maskedViews.updateAppInfo(oldAppInfo);
+ }
+ }
+ }
+ }
+
/**
* Updates all providers with the specified package names, and records any providers that were
* pruned.
@@ -4875,5 +4926,14 @@
public void unlockUser(int userId) {
handleUserUnlocked(userId);
}
+
+ @Override
+ public void applyResourceOverlaysToWidgets(Set<String> packageNames, int userId,
+ boolean updateFrameworkRes) {
+ synchronized (mLock) {
+ applyResourceOverlaysToWidgetsLocked(new HashSet<>(packageNames), userId,
+ updateFrameworkRes);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be..b7c61a0d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -2505,6 +2505,16 @@
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
}
+ private boolean isBleState(int state) {
+ switch (state) {
+ case BluetoothAdapter.STATE_BLE_ON:
+ case BluetoothAdapter.STATE_BLE_TURNING_ON:
+ case BluetoothAdapter.STATE_BLE_TURNING_OFF:
+ return true;
+ }
+ return false;
+ }
+
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
@@ -2527,8 +2537,15 @@
sendBluetoothServiceDownCallback();
unbindAndFinish();
sendBleStateChanged(prevState, newState);
- // Don't broadcast as it has already been broadcast before
- isStandardBroadcast = false;
+
+ /* Currently, the OFF intent is broadcasted externally only when we transition
+ * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
+ * we are guaranteed that the OFF intent has been broadcasted earlier and we
+ * can safely skip it.
+ * Conversely, if the previous state is not a BLE state, it indicates that some
+ * sort of crash has occurred, moving us directly to STATE_OFF without ever
+ * passing through BLE_ON. We should broadcast the OFF intent in this case. */
+ isStandardBroadcast = !isBleState(prevState);
} else if (!intermediate_off) {
// connect to GattService
@@ -2581,6 +2598,11 @@
// Show prevState of BLE_ON as OFF to standard users
prevState = BluetoothAdapter.STATE_OFF;
}
+ if (DBG) {
+ Slog.d(TAG,
+ "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
+ + BluetoothAdapter.nameForState(newState));
+ }
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 019e4ea..40e63da 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -21,8 +21,6 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.RunningServiceInfo;
import static android.app.ActivityManager.RunningTaskInfo;
-import static android.app.ActivityManager.getCurrentUser;
-import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
@@ -383,17 +381,9 @@
int sensor;
if (result == MODE_IGNORED) {
- if (code == OP_RECORD_AUDIO) {
+ if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE) {
sensor = MICROPHONE;
- } else if (code == OP_CAMERA) {
- sensor = CAMERA;
- } else {
- return;
- }
- } else if (result == MODE_ALLOWED) {
- if (code == OP_PHONE_CALL_MICROPHONE) {
- sensor = MICROPHONE;
- } else if (code == OP_PHONE_CALL_CAMERA) {
+ } else if (code == OP_CAMERA || code == OP_PHONE_CALL_CAMERA) {
sensor = CAMERA;
} else {
return;
@@ -718,6 +708,9 @@
public void setIndividualSensorPrivacy(@UserIdInt int userId,
@SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
enforceManageSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
return;
}
@@ -843,6 +836,9 @@
public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId,
@SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
enforceManageSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
int parentId = mUserManagerInternal.getProfileParentId(userId);
forAllUsers(userId2 -> {
if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -896,6 +892,9 @@
@Override
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
enforceObserveSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
synchronized (mLock) {
return isIndividualSensorPrivacyEnabledLocked(userId, sensor);
}
@@ -1213,6 +1212,9 @@
public void suppressIndividualSensorPrivacyReminders(int userId, int sensor,
IBinder token, boolean suppress) {
enforceManageSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
Objects.requireNonNull(token);
Pair<Integer, UserHandle> key = new Pair<>(sensor, UserHandle.of(userId));
@@ -1265,10 +1267,14 @@
case MICROPHONE:
mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled,
mAppOpsRestrictionToken);
+ mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled,
+ mAppOpsRestrictionToken);
break;
case CAMERA:
mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled,
mAppOpsRestrictionToken);
+ mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_CAMERA, enabled,
+ mAppOpsRestrictionToken);
break;
}
}
@@ -1898,9 +1904,9 @@
if (!mIsInEmergencyCall) {
mIsInEmergencyCall = true;
if (mSensorPrivacyServiceImpl
- .isIndividualSensorPrivacyEnabled(getCurrentUser(), MICROPHONE)) {
+ .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE)) {
mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
- getCurrentUser(), OTHER, MICROPHONE, false);
+ mCurrentUser, OTHER, MICROPHONE, false);
mMicUnmutedForEmergencyCall = true;
} else {
mMicUnmutedForEmergencyCall = false;
@@ -1915,7 +1921,7 @@
mIsInEmergencyCall = false;
if (mMicUnmutedForEmergencyCall) {
mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
- getCurrentUser(), OTHER, MICROPHONE, true);
+ mCurrentUser, OTHER, MICROPHONE, true);
mMicUnmutedForEmergencyCall = false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 99ae52c..e0df4b7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -193,6 +193,7 @@
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetManagerInternal;
import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
@@ -16597,6 +16598,13 @@
if (updateFrameworkRes) {
ParsingPackageUtils.readConfigUseRoundIcon(null);
}
+
+ AppWidgetManagerInternal widgets = LocalServices.getService(AppWidgetManagerInternal.class);
+ if (widgets != null) {
+ widgets.applyResourceOverlaysToWidgets(new HashSet<>(packagesToUpdate), userId,
+ updateFrameworkRes);
+ }
+
mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes);
if (updateFrameworkRes) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9f41c8b..ae14ca7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -841,6 +841,12 @@
public void noteEvent(final int code, final String name, final int uid) {
enforceCallingPermission();
+ if (name == null) {
+ // TODO(b/194733136): Replace with an IllegalArgumentException throw.
+ Slog.wtfStack(TAG, "noteEvent called with null name. code = " + code);
+ return;
+ }
+
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9dbb707..7c336d7 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
@@ -319,6 +320,7 @@
private int mPersistentCompactionCount;
private int mBfgsCompactionCount;
private final ProcessDependencies mProcessDependencies;
+ private final ProcLocksReader mProcLocksReader;
public CachedAppOptimizer(ActivityManagerService am) {
this(am, null, new DefaultProcessDependencies());
@@ -335,6 +337,7 @@
mProcessDependencies = processDependencies;
mTestCallback = callback;
mSettingsObserver = new SettingsContentObserver();
+ mProcLocksReader = new ProcLocksReader();
}
/**
@@ -1312,7 +1315,7 @@
try {
// pre-check for locks to avoid unnecessary freeze/unfreeze operations
- if (Process.hasFileLocks(pid)) {
+ if (mProcLocksReader.hasFileLocks(pid)) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
}
@@ -1399,7 +1402,7 @@
try {
// post-check to prevent races
- if (Process.hasFileLocks(pid)) {
+ if (mProcLocksReader.hasFileLocks(pid)) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4973d45..b1d300c 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1106,6 +1106,11 @@
return Settings.Secure.getInt(context.getContentResolver(),
CoexCoordinator.SETTING_ENABLE_NAME, 1) != 0;
}
+
+ public boolean isCoexFaceNonBypassHapticsDisabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
+ }
}
/**
@@ -1137,6 +1142,8 @@
// by default.
CoexCoordinator coexCoordinator = CoexCoordinator.getInstance();
coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context));
+ coexCoordinator.setFaceHapticDisabledWhenNonBypass(
+ injector.isCoexFaceNonBypassHapticsDisabled(context));
try {
injector.getActivityManagerService().registerUserSwitchObserver(
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 013c74d..f4327e8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -54,12 +54,18 @@
public static final int STATE_NEW = 0;
// Framework/HAL have started this operation
public static final int STATE_STARTED = 1;
- // Operation is started, but requires some user action (such as finger lift & re-touch)
+ // Operation is started, but requires some user action to start (such as finger lift & re-touch)
public static final int STATE_STARTED_PAUSED = 2;
+ // Same as above, except auth was attempted (rejected, timed out, etc).
+ public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3;
// Done, errored, canceled, etc. HAL/framework are not running this sensor anymore.
- public static final int STATE_STOPPED = 3;
+ public static final int STATE_STOPPED = 4;
- @IntDef({STATE_NEW, STATE_STARTED, STATE_STARTED_PAUSED, STATE_STOPPED})
+ @IntDef({STATE_NEW,
+ STATE_STARTED,
+ STATE_STARTED_PAUSED,
+ STATE_STARTED_PAUSED_ATTEMPTED,
+ STATE_STOPPED})
@interface State {}
private final boolean mIsStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
index b576673..a15ecad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
@@ -45,6 +45,8 @@
private static final String TAG = "BiometricCoexCoordinator";
public static final String SETTING_ENABLE_NAME =
"com.android.server.biometrics.sensors.CoexCoordinator.enable";
+ public static final String FACE_HAPTIC_DISABLE =
+ "com.android.server.biometrics.sensors.CoexCoordinator.disable_face_haptics";
private static final boolean DEBUG = true;
// Successful authentications should be used within this amount of time.
@@ -144,6 +146,10 @@
mAdvancedLogicEnabled = enabled;
}
+ public void setFaceHapticDisabledWhenNonBypass(boolean disabled) {
+ mFaceHapticDisabledWhenNonBypass = disabled;
+ }
+
@VisibleForTesting
void reset() {
mClientMap.clear();
@@ -153,6 +159,7 @@
private final Map<Integer, AuthenticationClient<?>> mClientMap;
@VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths;
private boolean mAdvancedLogicEnabled;
+ private boolean mFaceHapticDisabledWhenNonBypass;
private final Handler mHandler;
private CoexCoordinator() {
@@ -225,7 +232,11 @@
mSuccessfulAuths.add(new SuccessfulAuth(mHandler, mSuccessfulAuths,
currentTimeMillis, SENSOR_TYPE_FACE, client, callback));
} else {
- callback.sendHapticFeedback();
+ if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+ Slog.w(TAG, "Skipping face success haptic");
+ } else {
+ callback.sendHapticFeedback();
+ }
callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
callback.handleLifecycleAfterAuth();
}
@@ -278,12 +289,23 @@
// BiometricScheduler do not get stuck.
Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
callback.handleLifecycleAfterAuth();
- } else {
- // UDFPS is not actively authenticating (finger not touching, already
- // rejected, etc).
+ } else if (isUdfpsAuthAttempted(udfps)) {
+ // If UDFPS is STATE_STARTED_PAUSED (e.g. finger rejected but can still
+ // auth after pointer goes down, it means UDFPS encountered a rejection. In
+ // this case, we need to play the final reject haptic since face auth is
+ // also done now.
callback.sendHapticFeedback();
callback.handleLifecycleAfterAuth();
}
+ else {
+ // UDFPS auth has never been attempted.
+ if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+ Slog.w(TAG, "Skipping face reject haptic");
+ } else {
+ callback.sendHapticFeedback();
+ }
+ callback.handleLifecycleAfterAuth();
+ }
} else if (isCurrentUdfps(client)) {
// Face should either be running, or have already finished
SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis);
@@ -374,6 +396,13 @@
return false;
}
+ private static boolean isUdfpsAuthAttempted(@Nullable AuthenticationClient<?> client) {
+ if (client instanceof Udfps) {
+ return client.getState() == AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED;
+ }
+ return false;
+ }
+
private boolean isUnknownClient(@NonNull AuthenticationClient<?> client) {
for (AuthenticationClient<?> c : mClientMap.values()) {
if (c == client) {
@@ -400,6 +429,7 @@
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Enabled: ").append(mAdvancedLogicEnabled);
+ sb.append(", Face Haptic Disabled: ").append(mFaceHapticDisabledWhenNonBypass);
sb.append(", Queue size: " ).append(mSuccessfulAuths.size());
for (SuccessfulAuth auth : mSuccessfulAuths) {
sb.append(", Auth: ").append(auth.toString());
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 99e6e62..8835c1e0 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
@@ -114,7 +114,7 @@
mState = STATE_STOPPED;
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
} else {
- mState = STATE_STARTED_PAUSED;
+ mState = STATE_STARTED_PAUSED_ATTEMPTED;
}
}
@@ -188,7 +188,7 @@
public void onPointerUp() {
try {
mIsPointerDown = false;
- mState = STATE_STARTED_PAUSED;
+ mState = STATE_STARTED_PAUSED_ATTEMPTED;
mALSProbeCallback.getProbe().disable();
getFreshDaemon().onPointerUp(0 /* pointerId */);
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 7558d15..83f1480 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
@@ -112,7 +112,7 @@
resetFailedAttempts(getTargetUserId());
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
} else {
- mState = STATE_STARTED_PAUSED;
+ mState = STATE_STARTED_PAUSED_ATTEMPTED;
final @LockoutTracker.LockoutMode int lockoutMode =
mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
@@ -206,7 +206,7 @@
@Override
public void onPointerUp() {
mIsPointerDown = false;
- mState = STATE_STARTED_PAUSED;
+ mState = STATE_STARTED_PAUSED_ATTEMPTED;
mALSProbeCallback.getProbe().disable();
UdfpsHelper.onFingerUp(getFreshDaemon());
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4ee867b..097b071 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -289,8 +289,7 @@
private String mActiveIface;
/** Set of any ifaces associated with mobile networks since boot. */
- @GuardedBy("mStatsLock")
- private String[] mMobileIfaces = new String[0];
+ private volatile String[] mMobileIfaces = new String[0];
/** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
@GuardedBy("mStatsLock")
@@ -935,7 +934,12 @@
@Override
public String[] getMobileIfaces() {
- return mMobileIfaces;
+ // TODO (b/192758557): Remove debug log.
+ if (ArrayUtils.contains(mMobileIfaces, null)) {
+ throw new NullPointerException(
+ "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+ }
+ return mMobileIfaces.clone();
}
@Override
@@ -1084,7 +1088,8 @@
}
@Override
- public long getIfaceStats(String iface, int type) {
+ public long getIfaceStats(@NonNull String iface, int type) {
+ Objects.requireNonNull(iface);
long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
if (nativeIfaceStats == -1) {
return nativeIfaceStats;
@@ -1382,7 +1387,12 @@
}
}
- mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
+ mMobileIfaces = mobileIfaces.toArray(new String[0]);
+ // TODO (b/192758557): Remove debug log.
+ if (ArrayUtils.contains(mMobileIfaces, null)) {
+ throw new NullPointerException(
+ "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+ }
}
private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee44c10..031910a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24838,11 +24838,11 @@
pw.println("vers,1");
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_VERSION)
- && packageName == null) {
- // dump version information for all volumes with installed packages
- dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+ // reader
+ if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+ if (!checkin) {
+ dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+ }
}
if (!checkin
@@ -24873,8 +24873,7 @@
ipw.decreaseIndent();
}
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
final String requiredVerifierPackage = mRequiredVerifierPackage;
if (!checkin) {
if (dumpState.onTitlePrinted()) {
@@ -24895,8 +24894,7 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
final ComponentName verifierComponent = proxy.getComponentName();
if (verifierComponent != null) {
@@ -24923,13 +24921,11 @@
}
}
- if (dumpState.isDumping(DumpState.DUMP_LIBS)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_FEATURES)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
if (dumpState.onTitlePrinted()) {
pw.println();
}
@@ -24939,7 +24935,12 @@
synchronized (mAvailableFeatures) {
for (FeatureInfo feat : mAvailableFeatures.values()) {
- if (!checkin) {
+ if (checkin) {
+ pw.print("feat,");
+ pw.print(feat.name);
+ pw.print(",");
+ pw.println(feat.version);
+ } else {
pw.print(" ");
pw.print(feat.name);
if (feat.version > 0) {
@@ -24947,73 +24948,55 @@
pw.print(feat.version);
}
pw.println();
- } else {
- pw.print("feat,");
- pw.print(feat.name);
- pw.print(",");
- pw.println(feat.version);
}
}
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
synchronized (mLock) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PREFERRED)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
synchronized (mLock) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
synchronized (mLock) {
mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
@@ -25028,15 +25011,11 @@
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_QUERIES)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
// This cannot be moved to ComputerEngine since the set of packages in the
// SharedUserSetting do not have a copy.
synchronized (mLock) {
@@ -25044,9 +25023,7 @@
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_CHANGES)
- && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
if (dumpState.onTitlePrinted()) pw.println();
pw.println("Package Changes:");
synchronized (mLock) {
@@ -25073,9 +25050,7 @@
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_FROZEN)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
@@ -25096,9 +25071,7 @@
ipw.decreaseIndent();
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_VOLUMES)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
if (dumpState.onTitlePrinted()) pw.println();
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
@@ -25117,61 +25090,50 @@
ipw.decreaseIndent();
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
&& packageName == null) {
synchronized (mLock) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_DEXOPT)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
if (dumpState.onTitlePrinted()) pw.println();
dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
if (dumpState.onTitlePrinted()) pw.println();
dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
- && packageName == null) {
- if (!checkin) {
- if (dumpState.onTitlePrinted()) pw.println();
- synchronized (mLock) {
- mSettings.dumpReadMessagesLPr(pw, dumpState);
- }
- pw.println();
- pw.println("Package warning messages:");
- dumpCriticalInfo(pw, null);
- } else {
- dumpCriticalInfo(pw, "msg,");
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ synchronized (mLock) {
+ mSettings.dumpReadMessagesLPr(pw, dumpState);
}
+ pw.println();
+ pw.println("Package warning messages:");
+ dumpCriticalInfo(pw, null);
+ }
+
+ if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
+ dumpCriticalInfo(pw, "msg,");
}
// PackageInstaller should be called outside of mPackages lock
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_INSTALLS)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
// XXX should handle packageName != null by dumping only install data that
// the given package is involved with.
if (dumpState.onTitlePrinted()) pw.println();
mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120));
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_APEX)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
mApexManager.dump(pw, packageName);
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
&& packageName == null) {
pw.println();
pw.println("Per UID read timeouts:");
@@ -25190,9 +25152,7 @@
}
}
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
pw.println("Snapshot statistics");
if (!mSnapshotEnabled) {
pw.println(" Snapshots disabled");
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f2f1926..11a0fba 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -32,6 +32,10 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
@@ -211,7 +215,7 @@
InsetsState getInsetsForWindow(WindowState target) {
final InsetsState originalState = mStateController.getInsetsForWindow(target);
final InsetsState state = adjustVisibilityForTransientTypes(originalState);
- return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
+ return adjustVisibilityForIme(target, state, state == originalState);
}
/**
@@ -241,16 +245,38 @@
return state;
}
- // Navigation bar insets is always visible to IME.
- private static InsetsState adjustVisibilityForIme(InsetsState originalState,
+ private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
boolean copyState) {
- final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
- if (originalNavSource != null && !originalNavSource.isVisible()) {
- final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
- final InsetsSource navSource = new InsetsSource(originalNavSource);
- navSource.setVisible(true);
- state.addSource(navSource);
- return state;
+ if (w.mIsImWindow) {
+ // Navigation bar insets is always visible to IME.
+ final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
+ if (originalNavSource != null && !originalNavSource.isVisible()) {
+ final InsetsState state = copyState ? new InsetsState(originalState)
+ : originalState;
+ final InsetsSource navSource = new InsetsSource(originalNavSource);
+ navSource.setVisible(true);
+ state.addSource(navSource);
+ return state;
+ }
+ } else if (w.mActivityRecord != null && !w.mActivityRecord.mLastImeShown) {
+ // During switching tasks with gestural navigation, if the IME is attached to
+ // one app window on that time, even the next app window is behind the IME window,
+ // conceptually the window should not receive the IME insets if the next window is
+ // not eligible IME requester and ready to show IME on top of it.
+ final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
+ final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
+
+ if (originalImeSource != null && shouldImeAttachedToApp
+ && (w.isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS)
+ || !w.getRequestedVisibility(ITYPE_IME))) {
+ final InsetsState state = copyState ? new InsetsState(originalState)
+ : originalState;
+
+ final InsetsSource imeSource = new InsetsSource(originalImeSource);
+ imeSource.setVisible(false);
+ state.addSource(imeSource);
+ return state;
+ }
}
return originalState;
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
index f1adcae..7f5f3c2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
@@ -70,6 +70,7 @@
mCoexCoordinator = CoexCoordinator.getInstance();
mCoexCoordinator.setAdvancedLogicEnabled(true);
+ mCoexCoordinator.setFaceHapticDisabledWhenNonBypass(true);
}
@Test
@@ -151,12 +152,76 @@
mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
mCallback);
- verify(mCallback).sendHapticFeedback();
+ // Haptics tested in #testKeyguard_bypass_haptics. Let's leave this commented out (instead
+ // of removed) to keep this context.
+ // verify(mCallback).sendHapticFeedback();
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
verify(mCallback).handleLifecycleAfterAuth();
}
@Test
+ public void testKeyguard_faceAuthSuccess_nonBypass_udfpsRunning_noHaptics() {
+ testKeyguard_bypass_haptics(false /* bypassEnabled */,
+ true /* faceAccepted */,
+ false /* shouldReceiveHaptics */);
+ }
+
+ @Test
+ public void testKeyguard_faceAuthReject_nonBypass_udfpsRunning_noHaptics() {
+ testKeyguard_bypass_haptics(false /* bypassEnabled */,
+ false /* faceAccepted */,
+ false /* shouldReceiveHaptics */);
+ }
+
+ @Test
+ public void testKeyguard_faceAuthSuccess_bypass_udfpsRunning_haptics() {
+ testKeyguard_bypass_haptics(true /* bypassEnabled */,
+ true /* faceAccepted */,
+ true /* shouldReceiveHaptics */);
+ }
+
+ @Test
+ public void testKeyguard_faceAuthReject_bypass_udfpsRunning_haptics() {
+ testKeyguard_bypass_haptics(true /* bypassEnabled */,
+ false /* faceAccepted */,
+ true /* shouldReceiveHaptics */);
+ }
+
+ private void testKeyguard_bypass_haptics(boolean bypassEnabled, boolean faceAccepted,
+ boolean shouldReceiveHaptics) {
+ mCoexCoordinator.reset();
+
+ AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+ when(faceClient.isKeyguard()).thenReturn(true);
+ when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+
+ AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+ withSettings().extraInterfaces(Udfps.class));
+ when(udfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+ if (faceAccepted) {
+ mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
+ mCallback);
+ } else {
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ LockoutTracker.LOCKOUT_NONE, mCallback);
+ }
+
+ if (shouldReceiveHaptics) {
+ verify(mCallback).sendHapticFeedback();
+ } else {
+ verify(mCallback, never()).sendHapticFeedback();
+ }
+
+ verify(mCallback).sendAuthenticationResult(eq(faceAccepted) /* addAuthTokenIfStrong */);
+ verify(mCallback).handleLifecycleAfterAuth();
+ }
+
+ @Test
public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsRejectedWithinBounds() {
testKeyguard_faceAuth_udfpsTouching_faceSuccess(false /* thenUdfpsAccepted */,
0 /* udfpsRejectedAfterMs */);
@@ -294,12 +359,13 @@
}
@Test
- public void testKeyguard_udfpsRejected_thenFaceRejected() {
+ public void testKeyguard_udfpsRejected_thenFaceRejected_noKeyguardBypass() {
mCoexCoordinator.reset();
AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
when(faceClient.isKeyguard()).thenReturn(true);
when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(faceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
withSettings().extraInterfaces(Udfps.class));
@@ -312,8 +378,9 @@
mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient,
LockoutTracker.LOCKOUT_NONE, mCallback);
- // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic
- when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED);
+ // Auth was attempted
+ when(udfpsClient.getState())
+ .thenReturn(AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED);
verify(mCallback, never()).sendHapticFeedback();
verify(mCallback).handleLifecycleAfterAuth();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 92b670e..3a50bb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -53,6 +53,9 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
import static com.google.common.truth.Truth.assertThat;
@@ -891,6 +894,36 @@
assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testAdjustImeInsetsVisibilityWhenTaskSwitchIsAnimating() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+ final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+ controller.getImeSourceProvider().setWindow(mImeWindow, null, null);
+
+ // Simulate app requests IME with updating all windows Insets State when IME is above app.
+ mDisplayContent.setImeLayeringTarget(app);
+ mDisplayContent.setImeInputTarget(app);
+ assertTrue(mDisplayContent.shouldImeAttachedToApp());
+ controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+ controller.getImeSourceProvider().getSource().setVisible(true);
+ controller.updateAboveInsetsState(mImeWindow, false);
+
+ // Simulate task switching animation happens when switching app to app2.
+ spyOn(app);
+ spyOn(app2);
+ doReturn(true).when(app).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
+ doReturn(true).when(app2).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
+ app.mActivityRecord.mLastImeShown = true;
+
+ // Verify the IME insets is visible on app, but not for app2 during task animating.
+ InsetsState stateApp = app.getInsetsState();
+ InsetsState stateApp2 = app2.getInsetsState();
+ assertTrue(stateApp.getSource(ITYPE_IME).isVisible());
+ assertFalse(stateApp2.getSource(ITYPE_IME).isVisible());
+ }
+
@UseTestDisplay(addWindows = { W_ACTIVITY })
@Test
public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
index b9e1fcd..68b2e61 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
@@ -29,7 +29,6 @@
import android.media.permission.PermissionUtil;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceSpecificException;
import android.text.TextUtils;
import android.util.Slog;
@@ -117,8 +116,8 @@
/**
* Throws a {@link SecurityException} if originator permanently doesn't have the given
- * permission, or a {@link ServiceSpecificException} with a {@link
- * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission.
+ * permission.
+ * Soft (temporary) denials are considered OK for preflight purposes.
*
* @param context A {@link Context}, used for permission checks.
* @param identity The identity to check.
@@ -130,15 +129,12 @@
permission);
switch (status) {
case PermissionChecker.PERMISSION_GRANTED:
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
return;
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
permission, toString(identity)));
- case PermissionChecker.PERMISSION_SOFT_DENIED:
- throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED,
- TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
- permission, toString(identity)));
default:
throw new RuntimeException("Unexpected permission check result.");
}