diff options
97 files changed, 1433 insertions, 787 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index dbaf2757a9e2..0c64c86daf3e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5654,8 +5654,8 @@ public final class ActivityThread extends ClientTransactionHandler */ private void scheduleRelaunchActivityIfPossible(@NonNull ActivityClientRecord r, boolean preserveWindow) { - if (r.activity.mFinished || r.token instanceof Binder) { - // Do not schedule relaunch if the activity is finishing or not a local object (e.g. + if ((r.activity != null && r.activity.mFinished) || r.token instanceof Binder) { + // Do not schedule relaunch if the activity is finishing or is a local object (e.g. // created by ActivtiyGroup that server side doesn't recognize it). return; } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 4ddb546a351e..d932a29beca6 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2849,14 +2849,14 @@ public class AppOpsManager { private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>(); /** - * If a thread is currently executing a two-way binder transaction, this stores the - * ops that were noted blaming any app (the caller, the caller of the caller, etc). + * If a thread is currently executing a two-way binder transaction, this stores the op-codes of + * the app-ops that were noted during this transaction. * * @see #getNotedOpCollectionMode * @see #collectNotedOpSync */ - private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>> - sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>(); + private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction = + new ThreadLocal<>(); /** Whether noting for an appop should be collected */ private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP]; @@ -7205,6 +7205,34 @@ public class AppOpsManager { * @hide */ public interface OnOpStartedListener { + + /** + * Represents a start operation that was unsuccessful + * @hide + */ + public int START_TYPE_FAILED = 0; + + /** + * Represents a successful start operation + * @hide + */ + public int START_TYPE_STARTED = 1; + + /** + * Represents an operation where a restricted operation became unrestricted, and resumed. + * @hide + */ + public int START_TYPE_RESUMED = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + START_TYPE_FAILED, + START_TYPE_STARTED, + START_TYPE_RESUMED + }) + public @interface StartedType {} + /** * Called when an op was started. * @@ -7213,11 +7241,35 @@ public class AppOpsManager { * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param attributionTag The attribution tag performing the operation. - * @param flags The flags of this op + * @param flags The flags of this op. * @param result The result of the start. */ void onOpStarted(int op, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result); + + /** + * Called when an op was started. + * + * Note: This is only for op starts. It is not called when an op is noted or stopped. + * By default, unless this method is overridden, no code will be executed for resume + * events. + * @param op The op code. + * @param uid The UID performing the operation. + * @param packageName The package performing the operation. + * @param attributionTag The attribution tag performing the operation. + * @param flags The flags of this op. + * @param result The result of the start. + * @param startType The start type of this start event. Either failed, resumed, or started. + * @param attributionFlags The location of this started op in an attribution chain. + * @param attributionChainId The ID of the attribution chain of this op, if it is in one. + */ + default void onOpStarted(int op, int uid, String packageName, String attributionTag, + @OpFlags int flags, @Mode int result, @StartedType int startType, + @AttributionFlags int attributionFlags, int attributionChainId) { + if (startType != START_TYPE_RESUMED) { + onOpStarted(op, uid, packageName, attributionTag, flags, result); + } + } } AppOpsManager(Context context, IAppOpsService service) { @@ -7858,8 +7910,10 @@ public class AppOpsManager { cb = new IAppOpsStartedCallback.Stub() { @Override public void opStarted(int op, int uid, String packageName, String attributionTag, - int flags, int mode) { - callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode); + int flags, int mode, int startType, int attributionFlags, + int attributionChainId) { + callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode, + startType, attributionFlags, attributionChainId); } }; mStartedWatchers.put(callback, cb); @@ -9052,6 +9106,66 @@ public class AppOpsManager { } /** + * State of a temporarily paused noted app-ops collection. + * + * @see #pauseNotedAppOpsCollection() + * + * @hide + */ + public static class PausedNotedAppOpsCollection { + final int mUid; + final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps; + + PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String, + long[]> collectedNotedAppOps) { + mUid = uid; + mCollectedNotedAppOps = collectedNotedAppOps; + } + } + + /** + * Temporarily suspend collection of noted app-ops when binder-thread calls into the other + * process. During such a call there might be call-backs coming back on the same thread which + * should not be accounted to the current collection. + * + * @return a state needed to resume the collection + * + * @hide + */ + public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() { + Integer previousUid = sBinderThreadCallingUid.get(); + if (previousUid != null) { + ArrayMap<String, long[]> previousCollectedNotedAppOps = + sAppOpsNotedInThisBinderTransaction.get(); + + sBinderThreadCallingUid.remove(); + sAppOpsNotedInThisBinderTransaction.remove(); + + return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps); + } + + return null; + } + + /** + * Resume a collection paused via {@link #pauseNotedAppOpsCollection}. + * + * @param prevCollection The state of the previous collection + * + * @hide + */ + public static void resumeNotedAppOpsCollection( + @Nullable PausedNotedAppOpsCollection prevCollection) { + if (prevCollection != null) { + sBinderThreadCallingUid.set(prevCollection.mUid); + + if (prevCollection.mCollectedNotedAppOps != null) { + sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps); + } + } + } + + /** * Finish collection of noted appops on this thread. * * <p>Called at the end of a two way binder transaction. @@ -9091,47 +9205,26 @@ public class AppOpsManager { */ @TestApi public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) { - collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(), - syncOp.getPackageName()); - } - - /** - * Collect a noted op when inside of a two-way binder call. - * - * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded} - * - * @param code the op code to note for - * @param attributionTag the attribution tag to note for - * @param packageName the package to note for - */ - private static void collectNotedOpSync(int code, @Nullable String attributionTag, - @NonNull String packageName) { // If this is inside of a two-way binder call: // We are inside of a two-way binder call. Delivered to caller via // {@link #prefixParcelWithAppOpsIfNeeded} - ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted = - sAppOpsNotedInThisBinderTransaction.get(); + int op = sOpStrToOp.get(syncOp.getOp()); + ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get(); if (appOpsNoted == null) { appOpsNoted = new ArrayMap<>(1); sAppOpsNotedInThisBinderTransaction.set(appOpsNoted); } - ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName); - if (packageAppOpsNotedForAttribution == null) { - packageAppOpsNotedForAttribution = new ArrayMap<>(1); - appOpsNoted.put(packageName, packageAppOpsNotedForAttribution); - } - - long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag); + long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag()); if (appOpsNotedForAttribution == null) { appOpsNotedForAttribution = new long[2]; - packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution); + appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution); } - if (code < 64) { - appOpsNotedForAttribution[0] |= 1L << code; + if (op < 64) { + appOpsNotedForAttribution[0] |= 1L << op; } else { - appOpsNotedForAttribution[1] |= 1L << (code - 64); + appOpsNotedForAttribution[1] |= 1L << (op - 64); } } @@ -9185,7 +9278,9 @@ public class AppOpsManager { } } - if (isListeningForOpNotedInBinderTransaction()) { + Integer binderUid = sBinderThreadCallingUid.get(); + + if (binderUid != null && binderUid == uid) { return COLLECT_SYNC; } else { return COLLECT_ASYNC; @@ -9204,32 +9299,20 @@ public class AppOpsManager { */ // TODO (b/186872903) Refactor how sync noted ops are propagated. public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) { - if (!isListeningForOpNotedInBinderTransaction()) { - return; - } - final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps = - sAppOpsNotedInThisBinderTransaction.get(); + ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get(); if (notedAppOps == null) { return; } p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER); - final int packageCount = notedAppOps.size(); - p.writeInt(packageCount); + int numAttributionWithNotesAppOps = notedAppOps.size(); + p.writeInt(numAttributionWithNotesAppOps); - for (int i = 0; i < packageCount; i++) { + for (int i = 0; i < numAttributionWithNotesAppOps; i++) { p.writeString(notedAppOps.keyAt(i)); - - final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i); - final int tagCount = notedTagAppOps.size(); - p.writeInt(tagCount); - - for (int j = 0; j < tagCount; j++) { - p.writeString(notedTagAppOps.keyAt(j)); - p.writeLong(notedTagAppOps.valueAt(j)[0]); - p.writeLong(notedTagAppOps.valueAt(j)[1]); - } + p.writeLong(notedAppOps.valueAt(i)[0]); + p.writeLong(notedAppOps.valueAt(i)[1]); } } @@ -9244,55 +9327,37 @@ public class AppOpsManager { * @hide */ public static void readAndLogNotedAppops(@NonNull Parcel p) { - final int packageCount = p.readInt(); - if (packageCount <= 0) { - return; - } + int numAttributionsWithNotedAppOps = p.readInt(); - final String myPackageName = ActivityThread.currentPackageName(); + for (int i = 0; i < numAttributionsWithNotedAppOps; i++) { + String attributionTag = p.readString(); + long[] rawNotedAppOps = new long[2]; + rawNotedAppOps[0] = p.readLong(); + rawNotedAppOps[1] = p.readLong(); - synchronized (sLock) { - for (int i = 0; i < packageCount; i++) { - final String packageName = p.readString(); - - final int tagCount = p.readInt(); - for (int j = 0; j < tagCount; j++) { - final String attributionTag = p.readString(); - final long[] rawNotedAppOps = new long[2]; - rawNotedAppOps[0] = p.readLong(); - rawNotedAppOps[1] = p.readLong(); + if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) { + BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); - if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) { - continue; - } - - final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps); + synchronized (sLock) { for (int code = notedAppOps.nextSetBit(0); code != -1; code = notedAppOps.nextSetBit(code + 1)) { - if (Objects.equals(myPackageName, packageName)) { - if (sOnOpNotedCallback != null) { - sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, - attributionTag, packageName)); - } else { - String message = getFormattedStackTrace(); - sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(), - attributionTag, message, System.currentTimeMillis())); - if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) { - sUnforwardedOps.remove(0); - } + if (sOnOpNotedCallback != null) { + sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag)); + } else { + String message = getFormattedStackTrace(); + sUnforwardedOps.add( + new AsyncNotedAppOp(code, Process.myUid(), attributionTag, + message, System.currentTimeMillis())); + if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) { + sUnforwardedOps.remove(0); } - } else if (isListeningForOpNotedInBinderTransaction()) { - collectNotedOpSync(code, attributionTag, packageName); - } - } - for (int code = notedAppOps.nextSetBit(0); code != -1; - code = notedAppOps.nextSetBit(code + 1)) { - if (Objects.equals(myPackageName, packageName)) { - sMessageCollector.onNoted(new SyncNotedAppOp(code, - attributionTag, packageName)); } } } + for (int code = notedAppOps.nextSetBit(0); code != -1; + code = notedAppOps.nextSetBit(code + 1)) { + sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag)); + } } } } @@ -9398,15 +9463,7 @@ public class AppOpsManager { * @hide */ public static boolean isListeningForOpNoted() { - return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction() - || isCollectingStackTraces(); - } - - /** - * @return whether we are in a binder transaction and collecting appops. - */ - private static boolean isListeningForOpNotedInBinderTransaction() { - return sBinderThreadCallingUid.get() != null; + return sOnOpNotedCallback != null || isCollectingStackTraces(); } /** diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java index 32d889e81cb0..7c0c08a7fc35 100644 --- a/core/java/android/app/SyncNotedAppOp.java +++ b/core/java/android/app/SyncNotedAppOp.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcelable; -import android.os.Process; import com.android.internal.annotations.Immutable; import com.android.internal.util.DataClass; @@ -29,6 +28,8 @@ import com.android.internal.util.DataClass; /** * Description of an app-op that was noted for the current process. * + * Note: package name is currently unused in the system. + * * <p>This is either delivered after a * {@link AppOpsManager.OnOpNotedCallback#onNoted(SyncNotedAppOp) two way binder call} or * when the app diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java index 266e33af0e22..5694ca860453 100644 --- a/core/java/android/appwidget/AppWidgetManagerInternal.java +++ b/core/java/android/appwidget/AppWidgetManagerInternal.java @@ -19,8 +19,6 @@ package android.appwidget; import android.annotation.Nullable; import android.util.ArraySet; -import java.util.Set; - /** * App widget manager local system service interface. * @@ -44,16 +42,4 @@ public abstract class AppWidgetManagerInternal { * @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/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index a741f9649b4c..c714f507242e 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -745,9 +745,6 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall if (Binder.getCallingPid() == Process.myPid()) { return PermissionChecker.PERMISSION_GRANTED; } - if (!attributionSource.checkCallingUid()) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(getContext(), permission, -1, new AttributionSource(getContext().getAttributionSource(), attributionSource), /*message*/ null); diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java index 3af1b5b03d35..0dbf29de5253 100644 --- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java +++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java @@ -91,7 +91,7 @@ public class MultiResolutionImageReader implements AutoCloseable { * </p> * <p> * The {@code maxImages} parameter determines the maximum number of - * {@link Image} objects that can be be acquired from each of the {@code ImageReader} + * {@link Image} objects that can be acquired from each of the {@code ImageReader} * within the {@code MultiResolutionImageReader}. However, requesting more buffers will * use up more memory, so it is important to use only the minimum number necessary. The * application is strongly recommended to acquire no more than {@code maxImages} images diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index d5a35bc31e68..02245e49e611 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -446,16 +446,12 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } } - @Override - protected void finalize() throws Throwable { - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - } - super.finalize(); - } + public void release(boolean skipCloseNotification) { + boolean notifyClose = false; - public void release() { synchronized (mInterfaceLock) { + mHandlerThread.quitSafely(); + if (mSessionProcessor != null) { try { mSessionProcessor.deInitSession(); @@ -469,6 +465,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes if (mExtensionClientId >= 0) { CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); if (mInitialized) { + notifyClose = true; CameraExtensionCharacteristics.releaseSession(); } } @@ -482,6 +479,16 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes mClientRepeatingRequestSurface = null; mClientCaptureSurface = null; } + + if (notifyClose && !skipCloseNotification) { + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallbacks.onClosed( + CameraAdvancedExtensionSessionImpl.this)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } private void notifyConfigurationFailure() { @@ -491,7 +498,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes } } - release(); + release(true /*skipCloseNotification*/); final long ident = Binder.clearCallingIdentity(); try { @@ -507,15 +514,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes android.hardware.camera2.CameraCaptureSession.StateCallback { @Override public void onClosed(@NonNull CameraCaptureSession session) { - release(); - - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallbacks.onClosed( - CameraAdvancedExtensionSessionImpl.this)); - } finally { - Binder.restoreCallingIdentity(ident); - } + release(false /*skipCloseNotification*/); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 0bf812e03984..fc728a22ed5a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -697,12 +697,12 @@ public class CameraDeviceImpl extends CameraDevice } if (mCurrentExtensionSession != null) { - mCurrentExtensionSession.release(); + mCurrentExtensionSession.release(false /*skipCloseNotification*/); mCurrentExtensionSession = null; } if (mCurrentAdvancedExtensionSession != null) { - mCurrentAdvancedExtensionSession.release(); + mCurrentAdvancedExtensionSession.release(false /*skipCloseNotification*/); mCurrentAdvancedExtensionSession = null; } @@ -1352,12 +1352,12 @@ public class CameraDeviceImpl extends CameraDevice } if (mCurrentExtensionSession != null) { - mCurrentExtensionSession.release(); + mCurrentExtensionSession.release(true /*skipCloseNotification*/); mCurrentExtensionSession = null; } if (mCurrentAdvancedExtensionSession != null) { - mCurrentAdvancedExtensionSession.release(); + mCurrentAdvancedExtensionSession.release(true /*skipCloseNotification*/); mCurrentAdvancedExtensionSession = null; } diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 7d29a7d275cf..ecd24914c566 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -630,18 +630,13 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler); } - @Override - protected void finalize() throws Throwable { - if (mHandlerThread != null) { - mHandlerThread.quitSafely(); - } - super.finalize(); - } - /** @hide */ - public void release() { + public void release(boolean skipCloseNotification) { + boolean notifyClose = false; + synchronized (mInterfaceLock) { mInternalRepeatingRequestEnabled = false; + mHandlerThread.quitSafely(); try { mPreviewExtender.onDeInit(); @@ -654,6 +649,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { if (mExtensionClientId >= 0) { CameraExtensionCharacteristics.unregisterClient(mExtensionClientId); if (mInitialized) { + notifyClose = true; CameraExtensionCharacteristics.releaseSession(); } } @@ -704,6 +700,15 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mCameraRepeatingSurface = mClientRepeatingRequestSurface = null; mCameraBurstSurface = mClientCaptureSurface = null; } + + if (notifyClose && !skipCloseNotification) { + final long ident = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallbacks.onClosed(CameraExtensionSessionImpl.this)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } private void notifyConfigurationFailure() { @@ -713,7 +718,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } } - release(); + release(true /*skipCloseNotification*/); final long ident = Binder.clearCallingIdentity(); try { @@ -745,14 +750,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { android.hardware.camera2.CameraCaptureSession.StateCallback { @Override public void onClosed(@NonNull CameraCaptureSession session) { - release(); - - final long ident = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> mCallbacks.onClosed(CameraExtensionSessionImpl.this)); - } finally { - Binder.restoreCallingIdentity(ident); - } + release(false /*skipCloseNotification*/); } @Override diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java index 9acf9bf0c803..afefcbe80bf1 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java @@ -75,7 +75,7 @@ public final class CameraExtensionUtils { ImageWriter writer = null; Image img = null; SurfaceInfo surfaceInfo = new SurfaceInfo(); - int nativeFormat = SurfaceUtils.getSurfaceFormat(s); + int nativeFormat = SurfaceUtils.detectSurfaceFormat(s); int dataspace = SurfaceUtils.getSurfaceDataspace(s); Size surfaceSize = SurfaceUtils.getSurfaceSize(s); surfaceInfo.mFormat = nativeFormat; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 8fd9a6abf7b6..196134b397cb 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1720,21 +1720,21 @@ public class CameraMetadataNative implements Parcelable { new SetCommand() { @Override public <T> void setValue(CameraMetadataNative metadata, T value) { - metadata.setAWBRegions((MeteringRectangle[]) value); + metadata.setAWBRegions(value); } }); sSetCommandMap.put(CaptureRequest.CONTROL_AF_REGIONS.getNativeKey(), new SetCommand() { @Override public <T> void setValue(CameraMetadataNative metadata, T value) { - metadata.setAFRegions((MeteringRectangle[]) value); + metadata.setAFRegions(value); } }); sSetCommandMap.put(CaptureRequest.CONTROL_AE_REGIONS.getNativeKey(), new SetCommand() { @Override public <T> void setValue(CameraMetadataNative metadata, T value) { - metadata.setAERegions((MeteringRectangle[]) value); + metadata.setAERegions(value); } }); } @@ -1815,30 +1815,33 @@ public class CameraMetadataNative implements Parcelable { return true; } - private <T> boolean setAFRegions(MeteringRectangle[] afRegions) { + private <T> boolean setAFRegions(T afRegions) { if (afRegions == null) { return false; } setBase(CaptureRequest.CONTROL_AF_REGIONS_SET, true); - setBase(CaptureRequest.CONTROL_AF_REGIONS, afRegions); + // The cast to CaptureRequest.Key is needed since java does not support template + // specialization and we need to route this method to + // setBase(CaptureRequest.Key<T> key, T value) + setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AF_REGIONS, afRegions); return true; } - private <T> boolean setAERegions(MeteringRectangle[] aeRegions) { + private <T> boolean setAERegions(T aeRegions) { if (aeRegions == null) { return false; } setBase(CaptureRequest.CONTROL_AE_REGIONS_SET, true); - setBase(CaptureRequest.CONTROL_AE_REGIONS, aeRegions); + setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AE_REGIONS, aeRegions); return true; } - private <T> boolean setAWBRegions(MeteringRectangle[] awbRegions) { + private <T> boolean setAWBRegions(T awbRegions) { if (awbRegions == null) { return false; } setBase(CaptureRequest.CONTROL_AWB_REGIONS_SET, true); - setBase(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions); + setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AWB_REGIONS, awbRegions); return true; } diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index 8dfc0a7b41ca..70c85a1fb26c 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -42,7 +42,7 @@ public final class InputConfiguration { private final boolean mIsMultiResolution; /** - * Create an input configration with the width, height, and user-defined format. + * Create an input configuration with the width, height, and user-defined format. * * <p>Images of a user-defined format are accessible by applications. Use * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP} @@ -64,7 +64,7 @@ public final class InputConfiguration { } /** - * Create an input configration with the format and a list of multi-resolution input stream + * Create an input configuration with the format and a list of multi-resolution input stream * info. * * <p>Use {@link @@ -108,7 +108,7 @@ public final class InputConfiguration { } /** - * Get the width of this input configration. + * Get the width of this input configuration. * * @return width of this input configuration. */ @@ -117,7 +117,7 @@ public final class InputConfiguration { } /** - * Get the height of this input configration. + * Get the height of this input configuration. * * @return height of this input configuration. */ @@ -126,7 +126,7 @@ public final class InputConfiguration { } /** - * Get the format of this input configration. + * Get the format of this input configuration. * * @return format of this input configuration. */ diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index 57d8ded79e8e..fd1a33161740 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -160,6 +160,23 @@ public class SurfaceUtils { } /** + * Detect and retrieve the Surface format without any + * additional overrides. + * + * @param surface The surface to be queried for format. + * @return format of the surface. + * + * @throws IllegalArgumentException if the surface is already abandoned. + */ + public static int detectSurfaceFormat(Surface surface) { + checkNotNull(surface); + int surfaceType = nativeDetectSurfaceType(surface); + if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned"); + + return surfaceType; + } + + /** * Get the Surface dataspace. * * @param surface The surface to be queried for dataspace. diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index d44b016cb5d0..3d466a0bf007 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -560,6 +560,9 @@ public final class BinderProxy implements IBinder { } } + final AppOpsManager.PausedNotedAppOpsCollection prevCollection = + AppOpsManager.pauseNotedAppOpsCollection(); + if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) { flags |= FLAG_COLLECT_NOTED_APP_OPS; } @@ -567,6 +570,8 @@ public final class BinderProxy implements IBinder { try { return transactNative(code, data, reply, flags); } finally { + AppOpsManager.resumeNotedAppOpsCollection(prevCollection); + if (transactListener != null) { transactListener.onTransactEnded(session); } diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 03f94c549512..19f204b377c8 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -31,7 +31,9 @@ import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; +import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.media.AudioSystem.MODE_IN_COMMUNICATION; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; @@ -63,7 +65,8 @@ import java.util.Objects; * * @hide */ -public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener { +public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener, + AppOpsManager.OnOpStartedListener { /** Whether to show the mic and camera icons. */ private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"; @@ -160,9 +163,10 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis mUserContexts = new ArrayMap<>(); mUserContexts.put(Process.myUserHandle(), mContext); // TODO ntmyren: make this listen for flag enable/disable changes - String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; - mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops, - context.getMainExecutor(), this); + String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; + mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this); + int[] ops = { OP_CAMERA, OP_RECORD_AUDIO }; + mAppOpsManager.startWatchingStarted(ops, this); } private Context getUserContext(UserHandle user) { @@ -182,25 +186,65 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags, int attributionChainId) { - if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE - || attributionFlags == ATTRIBUTION_FLAGS_NONE - || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { - // If this is not a chain, or it is untrusted, return + if (active) { + // Started callback handles these return; } - if (!active) { - // if any link in the chain is finished, remove the chain. - // TODO ntmyren: be smarter about this - mAttributionChains.remove(attributionChainId); + // if any link in the chain is finished, remove the chain. Then, find any other chains that + // contain this op/package/uid/tag combination, and remove them, as well. + // TODO ntmyren: be smarter about this + mAttributionChains.remove(attributionChainId); + int numChains = mAttributionChains.size(); + ArrayList<Integer> toRemove = new ArrayList<>(); + for (int i = 0; i < numChains; i++) { + int chainId = mAttributionChains.keyAt(i); + ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i); + int chainSize = chain.size(); + for (int j = 0; j < chainSize; j++) { + AccessChainLink link = chain.get(j); + if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) { + toRemove.add(chainId); + break; + } + } + } + mAttributionChains.removeAll(toRemove); + } + + @Override + public void onOpStarted(int op, int uid, String packageName, String attributionTag, + @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { + // not part of an attribution chain. Do nothing + } + + @Override + public void onOpStarted(int op, int uid, String packageName, String attributionTag, + @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result, + @StartedType int startedType, @AttributionFlags int attributionFlags, + int attributionChainId) { + if (startedType == START_TYPE_FAILED || attributionChainId == ATTRIBUTION_CHAIN_ID_NONE + || attributionFlags == ATTRIBUTION_FLAGS_NONE + || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { + // If this is not a successful start, or it is not a chain, or it is untrusted, return return; } + addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid, + attributionTag, attributionFlags, attributionChainId); + } + + private void addLinkToChainIfNotPresent(String op, String packageName, int uid, + String attributionTag, int attributionFlags, int attributionChainId) { ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent( attributionChainId, k -> new ArrayList<>()); AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid, attributionFlags); + if (currentChain.contains(link)) { + return; + } + int currSize = currentChain.size(); if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) { // if the list is empty, this link is the end, or the last link in the current chain @@ -613,5 +657,21 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public boolean isStart() { return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0; } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AccessChainLink)) { + return false; + } + AccessChainLink other = (AccessChainLink) obj; + return other.flags == flags && packageAndOpEquals(other.usage.op, + other.usage.packageName, other.usage.attributionTag, other.usage.uid); + } + + public boolean packageAndOpEquals(String op, String packageName, String attributionTag, + int uid) { + return Objects.equals(op, usage.op) && Objects.equals(packageName, usage.packageName) + && Objects.equals(attributionTag, usage.attributionTag) && uid == usage.uid; + } } } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 41374167cc56..face870ca1b4 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -783,6 +783,9 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * This may happen if another detector has been instantiated or the * {@link VoiceInteractionService} hosting this detector has been shut down. */ + // TODO: Remove this RequiresPermission since it isn't actually enforced. Also fix the javadoc + // about permissions enforcement (when it throws vs when it just returns false) for other + // methods in this class. @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) @Override public boolean stopRecognition() { diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java index 02294e5720ae..f7a3415259fd 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -82,6 +82,9 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { try { mManagerService.startListeningFromMic( mAudioFormat, new BinderCallback(mHandler, mCallback)); + } catch (SecurityException e) { + Slog.e(TAG, "startRecognition failed: " + e); + return false; } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 6c2d6a1c3f2e..c1956e45653b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,6 +33,7 @@ import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; @@ -237,15 +238,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall new SurfaceControl.Transaction(); /** - * Transaction that should be used for - * {@link RenderNode.PositionUpdateListener#positionChanged(long, int, int, int, int)} - * The callback is invoked from a thread pool so it's not thread safe with other render thread - * transactions. Keep the transactions for position changed callbacks on its own transaction. - */ - private final SurfaceControl.Transaction mPositionChangedTransaction = - new SurfaceControl.Transaction(); - - /** * A temporary transaction holder that should only be used when applying right away. There * should be no assumption about thread safety for this transaction. */ @@ -295,7 +287,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall int defStyleRes, boolean disableBackgroundLayer) { super(context, attrs, defStyleAttr, defStyleRes); mUseBlastAdapter = useBlastAdapter(context); - mRenderNode.addPositionUpdateListener(mPositionListener); setWillNotDraw(true); mDisableBackgroundLayer = disableBackgroundLayer; @@ -943,6 +934,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + + // The position update listener is used to safely share the surface size between render thread + // workers and the UI thread. Both threads need to know the surface size to determine the scale. + // The parent layer scales the surface size to view size. The child (BBQ) layer scales + // the buffer to the surface size. Both scales along with the window crop must be applied + // synchronously otherwise we may see flickers. + // When the listener is updated, we will get at least a single position update call so we can + // guarantee any changes we post will be applied. + private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight, + @Nullable Transaction geometryTransaction) { + if (mPositionListener != null) { + mRenderNode.removePositionUpdateListener(mPositionListener); + } + mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight, + geometryTransaction); + mRenderNode.addPositionUpdateListener(mPositionListener); + } + private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, boolean creating, boolean sizeChanged, boolean hintChanged) { boolean realSizeChanged = false; @@ -985,13 +994,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // While creating the surface, we will set it's initial // geometry. Outside of that though, we should generally // leave it to the RenderThread. - // - // There is one more case when the buffer size changes we aren't yet - // prepared to sync (as even following the transaction applying - // we still need to latch a buffer). - // b/28866173 - if (sizeChanged || creating || !mRtHandlingPositionUpdates) { - onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl, + Transaction geometryTransaction = new Transaction(); + geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); + if ((sizeChanged || hintChanged) && !creating) { + setBufferSize(geometryTransaction); + } + if (sizeChanged || creating || !isHardwareAccelerated()) { + onSetSurfacePositionAndScaleRT(geometryTransaction, mSurfaceControl, mScreenRect.left, /*positionLeft*/ mScreenRect.top /*positionTop*/ , mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/, @@ -1002,17 +1011,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // use SCALING_MODE_SCALE and submit a larger size than the surface // size. if (mClipSurfaceToBounds && mClipBounds != null) { - mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds); + geometryTransaction.setWindowCrop(mSurfaceControl, mClipBounds); } else { - mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, + geometryTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, mSurfaceHeight); } - } - mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); - if ((sizeChanged || hintChanged) && !creating) { - setBufferSize(mTmpTransaction); - } + boolean applyChangesOnRenderThread = + sizeChanged && !creating && isHardwareAccelerated(); + if (isHardwareAccelerated()) { + // This will consume the passed in transaction and the transaction will be + // applied on a render worker thread. + replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight, + applyChangesOnRenderThread ? geometryTransaction : null); + } + if (DEBUG_POSITION) { + Log.d(TAG, String.format( + "%d updateSurfacePosition %s" + + "position = [%d, %d, %d, %d] surfaceSize = %dx%d", + System.identityHashCode(this), + applyChangesOnRenderThread ? "RenderWorker" : "UiThread", + mScreenRect.left, mScreenRect.top, mScreenRect.right, + mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight)); + } + } + mTmpTransaction.merge(geometryTransaction); mTmpTransaction.apply(); updateEmbeddedAccessibilityMatrix(); @@ -1399,19 +1422,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpTransaction.apply(); } - private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t, - Rect position) { - onSetSurfacePositionAndScaleRT(t, surface, - position.left /*positionLeft*/, - position.top /*positionTop*/, - position.width() / (float) mSurfaceWidth /*postScaleX*/, - position.height() / (float) mSurfaceHeight /*postScaleY*/); - - if (mViewVisibility) { - t.show(surface); - } - } - /** * @return The last render position of the backing surface or an empty rect. * @@ -1421,13 +1431,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return mRTLastReportedPosition; } - private void setParentSpaceRectangle(Rect position, long frameNumber, Transaction t) { - final ViewRootImpl viewRoot = getViewRootImpl(); - applySurfaceTransforms(mSurfaceControl, t, position); - applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber); - applyOrMergeTransaction(t, frameNumber); - } - private void applyOrMergeTransaction(Transaction t, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); boolean useBLAST = viewRoot != null && useBLASTSync(viewRoot); @@ -1440,9 +1443,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private Rect mRTLastReportedPosition = new Rect(); - - private RenderNode.PositionUpdateListener mPositionListener = - new RenderNode.PositionUpdateListener() { + private Point mRTLastReportedSurfaceSize = new Point(); + + private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener { + int mRtSurfaceWidth = -1; + int mRtSurfaceHeight = -1; + private final SurfaceControl.Transaction mPositionChangedTransaction = + new SurfaceControl.Transaction(); + boolean mPendingTransaction = false; + + SurfaceViewPositionUpdateListener(int surfaceWidth, int surfaceHeight, + @Nullable Transaction t) { + mRtSurfaceWidth = surfaceWidth; + mRtSurfaceHeight = surfaceHeight; + if (t != null) { + mPositionChangedTransaction.merge(t); + mPendingTransaction = true; + } + } @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { @@ -1464,21 +1482,34 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall if (mRTLastReportedPosition.left == left && mRTLastReportedPosition.top == top && mRTLastReportedPosition.right == right - && mRTLastReportedPosition.bottom == bottom) { + && mRTLastReportedPosition.bottom == bottom + && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth + && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight + && !mPendingTransaction) { return; } try { if (DEBUG_POSITION) { Log.d(TAG, String.format( "%d updateSurfacePosition RenderWorker, frameNr = %d, " - + "position = [%d, %d, %d, %d]", - System.identityHashCode(this), frameNumber, - left, top, right, bottom)); + + "position = [%d, %d, %d, %d] surfaceSize = %dx%d", + System.identityHashCode(SurfaceView.this), frameNumber, + left, top, right, bottom, mRtSurfaceWidth, mRtSurfaceHeight)); } mRTLastReportedPosition.set(left, top, right, bottom); - setParentSpaceRectangle(mRTLastReportedPosition, frameNumber, - mPositionChangedTransaction); - // Now overwrite mRTLastReportedPosition with our values + mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight); + onSetSurfacePositionAndScaleRT(mPositionChangedTransaction, mSurfaceControl, + mRTLastReportedPosition.left /*positionLeft*/, + mRTLastReportedPosition.top /*positionTop*/, + mRTLastReportedPosition.width() / (float) mRtSurfaceWidth /*postScaleX*/, + mRTLastReportedPosition.height() / (float) mRtSurfaceHeight /*postScaleY*/); + if (mViewVisibility) { + mPositionChangedTransaction.show(mSurfaceControl); + } + applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction, + getViewRootImpl().mSurface, frameNumber); + applyOrMergeTransaction(mPositionChangedTransaction, frameNumber); + mPendingTransaction = false; } catch (Exception ex) { Log.e(TAG, "Exception from repositionChild", ex); } @@ -1502,7 +1533,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall System.identityHashCode(this), frameNumber)); } mRTLastReportedPosition.setEmpty(); - + mRTLastReportedSurfaceSize.set(-1, -1); + if (mPendingTransaction) { + Log.w(TAG, System.identityHashCode(SurfaceView.this) + + "Pending transaction cleared."); + mPositionChangedTransaction.clear(); + mPendingTransaction = false; + } if (mSurfaceControl == null) { return; } @@ -1521,7 +1558,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mRtHandlingPositionUpdates = false; } } - }; + } + + private SurfaceViewPositionUpdateListener mPositionListener = null; private SurfaceHolder.Callback[] getSurfaceCallbacks() { SurfaceHolder.Callback[] callbacks; diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java index edd0d16a5ccb..606f39d6e25e 100644 --- a/core/java/android/view/translation/Translator.java +++ b/core/java/android/view/translation/Translator.java @@ -18,6 +18,7 @@ package android.view.translation; import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS; +import static android.view.translation.UiTranslationController.DEBUG; import android.annotation.CallbackExecutor; import android.annotation.NonNull; @@ -38,7 +39,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -395,27 +395,26 @@ public class Translator { private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub { - private final WeakReference<Consumer<TranslationResponse>> mCallback; - private final WeakReference<Executor> mExecutor; + private final Consumer<TranslationResponse> mCallback; + private final Executor mExecutor; TranslationResponseCallbackImpl(Consumer<TranslationResponse> callback, Executor executor) { - mCallback = new WeakReference<>(callback); - mExecutor = new WeakReference<>(executor); + mCallback = callback; + mExecutor = executor; } @Override public void onTranslationResponse(TranslationResponse response) throws RemoteException { - final Consumer<TranslationResponse> callback = mCallback.get(); + if (DEBUG) { + Log.i(TAG, "onTranslationResponse called."); + } final Runnable runnable = - () -> callback.accept(response); - if (callback != null) { - final Executor executor = mExecutor.get(); - final long token = Binder.clearCallingIdentity(); - try { - executor.execute(runnable); - } finally { - restoreCallingIdentity(token); - } + () -> mCallback.accept(response); + final long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(runnable); + } finally { + restoreCallingIdentity(token); } } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 91fc5a56d979..e827f0a31bfd 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5824,25 +5824,6 @@ public class RemoteViews implements Parcelable, Filter { 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/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl index 3a108e7e1d94..06640cb4ee06 100644 --- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl +++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl @@ -18,5 +18,6 @@ package com.android.internal.app; // Iterface to observe op starts oneway interface IAppOpsStartedCallback { - void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode); + void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode, + int startedType, int attributionFlags, int attributionChainId); } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 16ee6a745e1d..4b93363b5b90 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2429,6 +2429,12 @@ android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid) return (jint)nativeToJavaStatus(status); } +static jint android_media_AudioSystem_setHotwordDetectionServiceUid(JNIEnv *env, jobject thiz, + jint uid) { + status_t status = AudioSystem::setHotwordDetectionServiceUid(uid); + return (jint)nativeToJavaStatus(status); +} + static jint android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) { std::vector<uid_t> nativeUidsVector; @@ -2799,6 +2805,8 @@ static const JNINativeMethod gMethods[] = {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled}, {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid}, + {"setHotwordDetectionServiceUid", "(I)I", + (void *)android_media_AudioSystem_setHotwordDetectionServiceUid}, {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 95c24ad4a115..bad79eb13749 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -550,6 +550,8 @@ <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" /> <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" /> <protected-broadcast android:name="com.android.settings.bluetooth.ACTION_DISMISS_PAIRING" /> + <protected-broadcast android:name="com.android.settings.network.DELETE_SUBSCRIPTION" /> + <protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" /> <protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" /> <protected-broadcast android:name="NotificationManagerService.TIMEOUT" /> diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml index aa050f3a364d..2b9f95227f08 100644 --- a/core/res/res/layout/splash_screen_view.xml +++ b/core/res/res/layout/splash_screen_view.xml @@ -26,6 +26,7 @@ android:layout_width="wrap_content" android:layout_gravity="center" android:padding="0dp" + android:background="@null" android:contentDescription="@string/splash_screen_view_icon_description"/> <View android:id="@+id/splashscreen_branding_view" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 199e82f9d170..865f02663299 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1937,7 +1937,7 @@ <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-Warnung"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeitsprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Gewarnt"</string> - <string name="notification_verified_content_description" msgid="6401483602782359391">"Bestätigt"</string> + <string name="notification_verified_content_description" msgid="6401483602782359391">"Verifiziert"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Maximieren"</string> <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Minimieren"</string> <string name="expand_action_accessibility" msgid="1947657036871746627">"Maximierung ein-/auschalten"</string> diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java index 492520910afd..872331c82603 100644 --- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java +++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java @@ -67,7 +67,7 @@ public final class RippleAnimationSession { @NonNull RippleAnimationSession enter(Canvas canvas) { mStartTime = AnimationUtils.currentAnimationTimeMillis(); - if (isHwAccelerated(canvas)) { + if (useRTAnimations(canvas)) { enterHardware((RecordingCanvas) canvas); } else { enterSoftware(); @@ -82,7 +82,7 @@ public final class RippleAnimationSession { } @NonNull RippleAnimationSession exit(Canvas canvas) { - if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas); + if (useRTAnimations(canvas)) exitHardware((RecordingCanvas) canvas); else exitSoftware(); return this; } @@ -102,8 +102,12 @@ public final class RippleAnimationSession { return this; } - private boolean isHwAccelerated(Canvas canvas) { - return canvas.isHardwareAccelerated() && !mForceSoftware; + private boolean useRTAnimations(Canvas canvas) { + if (mForceSoftware) return false; + if (!canvas.isHardwareAccelerated()) return false; + RecordingCanvas hwCanvas = (RecordingCanvas) canvas; + if (hwCanvas.mNode == null || !hwCanvas.mNode.isAttached()) return false; + return true; } private void exitSoftware() { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index d3cff5cb81ff..7354c90c5c98 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -49,7 +49,9 @@ import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.Shader; import android.os.Build; +import android.os.Looper; import android.util.AttributeSet; +import android.util.Log; import android.view.animation.AnimationUtils; import android.view.animation.LinearInterpolator; @@ -113,6 +115,7 @@ import java.util.Arrays; * @attr ref android.R.styleable#RippleDrawable_color */ public class RippleDrawable extends LayerDrawable { + private static final String TAG = "RippleDrawable"; /** * Radius value that specifies the ripple radius should be computed based * on the size of the ripple's container. @@ -848,6 +851,10 @@ public class RippleDrawable extends LayerDrawable { private void startBackgroundAnimation() { mRunBackgroundAnimation = false; + if (Looper.myLooper() == null) { + Log.w(TAG, "Thread doesn't have a looper. Skipping animation."); + return; + } mBackgroundAnimation = ValueAnimator.ofFloat(mBackgroundOpacity, mTargetBackgroundOpacity); mBackgroundAnimation.setInterpolator(LINEAR_INTERPOLATOR); mBackgroundAnimation.setDuration(BACKGROUND_OPACITY_DURATION); diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 624b8b37a1ee..c2b6ffbd1048 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -31,7 +31,7 @@ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string> <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్స్టాచ్"</string> <string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్ పని చేయకపోవచ్చు."</string> - <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string> + <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్లో స్క్రీన్ విభజనకు మద్దతు లేదు."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్ప్లేలో యాప్ పని చేయకపోవచ్చు."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string> <string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index 60074b8cdb47..7cf4fb7a811d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -130,7 +130,7 @@ public final class OneHandedSettingsUtil { */ public boolean getSettingsTapsAppToExit(ContentResolver resolver, int userId) { return Settings.Secure.getIntForUser(resolver, - Settings.Secure.TAPS_APP_TO_EXIT, 0, userId) == 1; + Settings.Secure.TAPS_APP_TO_EXIT, 1, userId) == 1; } /** diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index c827932194ae..cb887f2d523d 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -38,6 +38,17 @@ public abstract class AudioManagerInternal { public abstract void updateRingerModeAffectedStreamsInternal(); + /** + * Notify the UID of the currently active {@link android.service.voice.HotwordDetectionService}. + * + * <p>The caller is expected to take care of any performance implications, e.g. by using a + * background thread to call this method.</p> + * + * @param uid UID of the currently active service or {@link android.os.Process#INVALID_UID} if + * none. + */ + public abstract void setHotwordDetectionServiceUid(int uid); + public abstract void setAccessibilityServiceUids(IntArray uids); /** diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8012f03d84b3..69d1889d5762 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1769,6 +1769,13 @@ public class AudioSystem public static native int setAssistantUid(int uid); /** + * Communicate UID of the current {@link android.service.voice.HotwordDetectionService} to audio + * policy service. + * @hide + */ + public static native int setHotwordDetectionServiceUid(int uid); + + /** * @hide * Communicate UIDs of active accessibility services to audio policy service. */ diff --git a/mms/OWNERS b/mms/OWNERS index befc320b949c..7f05a2a24d6e 100644 --- a/mms/OWNERS +++ b/mms/OWNERS @@ -2,7 +2,6 @@ set noparent tgunn@google.com breadley@google.com -hallliu@google.com rgreenwalt@google.com amitmahajan@google.com fionaxu@google.com @@ -10,7 +9,10 @@ jackyu@google.com jminjie@google.com satk@google.com shuoq@google.com -refuhoo@google.com nazaninb@google.com sarahchin@google.com -dbright@google.com
\ No newline at end of file +xiaotonj@google.com +huiwang@google.com +jayachandranc@google.com +chinmayd@google.com +amruthr@google.com diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS index 56688409bf78..0d23f053499c 100644 --- a/packages/CarrierDefaultApp/OWNERS +++ b/packages/CarrierDefaultApp/OWNERS @@ -1,7 +1,6 @@ set noparent tgunn@google.com breadley@google.com -hallliu@google.com rgreenwalt@google.com amitmahajan@google.com fionaxu@google.com @@ -9,9 +8,11 @@ jackyu@google.com jminjie@google.com satk@google.com shuoq@google.com -refuhoo@google.com nazaninb@google.com sarahchin@google.com -dbright@google.com xiaotonj@google.com +huiwang@google.com +jayachandranc@google.com +chinmayd@google.com +amruthr@google.com diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index a87002679408..4f74d2492d43 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -489,7 +489,7 @@ <string name="active_input_method_subtypes" msgid="4232680535471633046">"సక్రియ ఇన్పుట్ పద్ధతులు"</string> <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"సిస్టమ్ భాషలను ఉపయోగించు"</string> <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> యొక్క సెట్టింగ్లను తెరవడం విఫలమైంది"</string> - <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్వర్డ్లు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> అనువర్తనంలో అందించబడుతుంది. ఈ ఇన్పుట్ పద్ధతిని ఉపయోగించాలా?"</string> + <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్వర్డ్లు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> యాప్లో అందించబడుతుంది. ఈ ఇన్పుట్ పద్ధతిని ఉపయోగించాలా?"</string> <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"గమనిక: రీబూట్ చేసాక, మీరు మీ ఫోన్ను అన్లాక్ చేసే వరకు ఈ యాప్ ప్రారంభం కాదు"</string> <string name="ims_reg_title" msgid="8197592958123671062">"IMS నమోదు స్థితి"</string> <string name="ims_reg_status_registered" msgid="884916398194885457">"నమోదు చేయబడింది"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 79446e430d4f..ed8d524b0132 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -44,6 +44,8 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.RequiresApi; + import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -56,6 +58,7 @@ import java.util.concurrent.Executors; /** * InfoMediaManager provide interface to get InfoMediaDevice list. */ +@RequiresApi(Build.VERSION_CODES.R) public class InfoMediaManager extends MediaManager { private static final String TAG = "InfoMediaManager"; @@ -145,9 +148,16 @@ public class InfoMediaManager extends MediaManager { } private RoutingSessionInfo getRoutingSessionInfo() { + return getRoutingSessionInfo(mPackageName); + } + + private RoutingSessionInfo getRoutingSessionInfo(String packageName) { final List<RoutingSessionInfo> sessionInfos = - mRouterManager.getRoutingSessions(mPackageName); + mRouterManager.getRoutingSessions(packageName); + if (sessionInfos == null || sessionInfos.isEmpty()) { + return null; + } return sessionInfos.get(sessionInfos.size() - 1); } @@ -367,33 +377,13 @@ public class InfoMediaManager extends MediaManager { } boolean shouldDisableMediaOutput(String packageName) { - boolean shouldDisableMediaOutput = false; if (TextUtils.isEmpty(packageName)) { Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!"); - return false; - } - final List<MediaRoute2Info> infos = mRouterManager.getTransferableRoutes(packageName); - if (infos.size() == 1) { - final MediaRoute2Info info = infos.get(0); - final int deviceType = info.getType(); - switch (deviceType) { - case TYPE_UNKNOWN: - case TYPE_REMOTE_TV: - case TYPE_REMOTE_SPEAKER: - case TYPE_GROUP: - shouldDisableMediaOutput = true; - break; - default: - shouldDisableMediaOutput = false; - break; - } - } - if (DEBUG) { - Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size() - + ", package name : " + packageName + ", shouldDisableMediaOutput : " - + shouldDisableMediaOutput); + return true; } - return shouldDisableMediaOutput; + + // Disable when there is no transferable route + return mRouterManager.getTransferableRoutes(packageName).isEmpty(); } @TargetApi(Build.VERSION_CODES.R) @@ -456,7 +446,7 @@ public class InfoMediaManager extends MediaManager { } private void buildAvailableRoutes() { - for (MediaRoute2Info route : mRouterManager.getTransferableRoutes(mPackageName)) { + for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) { if (DEBUG) { Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : " + route.getVolume() + ", type : " + route.getType()); @@ -465,6 +455,29 @@ public class InfoMediaManager extends MediaManager { } } + private List<MediaRoute2Info> getAvailableRoutes(String packageName) { + final List<MediaRoute2Info> infos = new ArrayList<>(); + RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName); + if (routingSessionInfo != null) { + infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo)); + } + final List<MediaRoute2Info> transferableRoutes = + mRouterManager.getTransferableRoutes(packageName); + for (MediaRoute2Info transferableRoute : transferableRoutes) { + boolean alreadyAdded = false; + for (MediaRoute2Info mediaRoute2Info : infos) { + if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) { + alreadyAdded = true; + break; + } + } + if (!alreadyAdded) { + infos.add(transferableRoute); + } + } + return infos; + } + @VisibleForTesting void addMediaDevice(MediaRoute2Info route) { final int deviceType = route.getType(); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index a8da2c0ba269..22001c9c925a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -22,11 +22,13 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.media.RoutingSessionInfo; +import android.os.Build; import android.text.TextUtils; import android.util.Log; import androidx.annotation.IntDef; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.bluetooth.A2dpProfile; @@ -49,6 +51,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice. */ +@RequiresApi(Build.VERSION_CODES.R) public class LocalMediaManager implements BluetoothCallback { private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder(); private static final String TAG = "LocalMediaManager"; diff --git a/packages/SystemUI/res/anim/fp_to_unlock.xml b/packages/SystemUI/res/anim/fp_to_unlock.xml index a348208f48c7..a5f75b6726c8 100644 --- a/packages/SystemUI/res/anim/fp_to_unlock.xml +++ b/packages/SystemUI/res/anim/fp_to_unlock.xml @@ -19,10 +19,10 @@ <group android:name="_R_G"> <group android:name="_R_G_L_1_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5"> <group android:name="_R_G_L_1_G" android:translateX="30.75" android:translateY="25.75"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " /> - <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " /> - <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " /> - <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " /> + <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " /> + <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " /> + <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " /> + <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " /> </group> </group> <group android:name="_R_G_L_0_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5"> diff --git a/packages/SystemUI/res/anim/lock_to_unlock.xml b/packages/SystemUI/res/anim/lock_to_unlock.xml index ec51c0171709..76f7a05866d9 100644 --- a/packages/SystemUI/res/anim/lock_to_unlock.xml +++ b/packages/SystemUI/res/anim/lock_to_unlock.xml @@ -21,7 +21,7 @@ <group android:name="_R_G_L_2_G_N_10_T_1" android:translateX="50.25" android:translateY="61"> <group android:name="_R_G_L_2_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5"> <group android:name="_R_G_L_2_G" android:translateX="-0.375" android:translateY="-22.375"> - <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" 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 " /> + <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" 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> </group> @@ -30,7 +30,7 @@ <group android:name="_R_G_L_1_G_N_10_T_1" android:translateX="50.25" android:translateY="61"> <group android:name="_R_G_L_1_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5"> <group android:name="_R_G_L_1_G" android:translateX="5" android:translateY="-22.5"> - <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " /> + <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " /> </group> </group> </group> diff --git a/packages/SystemUI/res/drawable/ic_unlock.xml b/packages/SystemUI/res/drawable/ic_unlock.xml index c3b34699e8e2..46023e6160f5 100644 --- a/packages/SystemUI/res/drawable/ic_unlock.xml +++ b/packages/SystemUI/res/drawable/ic_unlock.xml @@ -21,7 +21,7 @@ android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" - android:strokeWidth="2.5" + android:strokeWidth="2" 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"> @@ -29,7 +29,7 @@ android:strokeColor="#FF000000" android:strokeLineCap="round" android:strokeLineJoin="round" - android:strokeWidth="2.5" + android:strokeWidth="2" 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"> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index b0f1f487b260..6d5be301a82d 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -68,7 +68,7 @@ android:id="@+id/lock_icon" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="48px" + android:padding="@dimen/lock_icon_padding" android:layout_gravity="center" android:scaleType="centerCrop"/> </com.android.keyguard.LockIconView> diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 1851790673e3..a9eb27ad3b02 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -34,7 +34,7 @@ android:id="@+id/udfps_aod_fp" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="48px" + android:padding="@dimen/lock_icon_padding" android:layout_gravity="center" android:scaleType="centerCrop" app:lottie_autoPlay="false" @@ -46,7 +46,7 @@ android:id="@+id/udfps_lockscreen_fp" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="48px" + android:padding="@dimen/lock_icon_padding" android:layout_gravity="center" android:scaleType="centerCrop" app:lottie_autoPlay="false" diff --git a/packages/SystemUI/res/raw/udfps_aod_fp.json b/packages/SystemUI/res/raw/udfps_aod_fp.json index 3247fe74fcfe..3b273ff92814 100644 --- a/packages/SystemUI/res/raw/udfps_aod_fp.json +++ b/packages/SystemUI/res/raw/udfps_aod_fp.json @@ -164,7 +164,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -526,7 +526,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -829,7 +829,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -1132,7 +1132,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -1507,7 +1507,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -1882,7 +1882,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -2257,7 +2257,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, @@ -2560,7 +2560,7 @@ }, "w":{ "a":0, - "k":1, + "k":1.3, "ix":5 }, "lc":2, diff --git a/packages/SystemUI/res/raw/udfps_lockscreen_fp.json b/packages/SystemUI/res/raw/udfps_lockscreen_fp.json index a25a47595fe7..a30a03a7e608 100644 --- a/packages/SystemUI/res/raw/udfps_lockscreen_fp.json +++ b/packages/SystemUI/res/raw/udfps_lockscreen_fp.json @@ -1 +1 @@ -{"v":"5.7.8","fr":60,"ip":0,"op":46,"w":46,"h":65,"nm":"fingerprint_build_on","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fingerprint_20210701 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.091,32.5,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999994755,0.949000000954,0.624000012875,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[{"tm":210,"cm":"2","dr":0},{"tm":255,"cm":"1","dr":0}]}
\ No newline at end of file +{"v":"5.7.8","fr":60,"ip":0,"op":46,"w":46,"h":65,"nm":"fingerprint_build_on","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fingerprint_20210701 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.091,32.5,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999994755,0.949000000954,0.624000012875,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[{"tm":210,"cm":"2","dr":0},{"tm":255,"cm":"1","dr":0}]}
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 34f8503ac1dd..0e02ad09d2c9 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -529,6 +529,9 @@ <!-- Gravity for the notification panel --> <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top --> + <!-- Padding for the lock icon on the keyguard. In pixels - should not scale with display size. --> + <dimen name="lock_icon_padding">48px</dimen> + <!-- Height of the carrier/wifi name label --> <dimen name="carrier_label_height">24dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 04e5dd15c5c5..a67021611812 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -438,6 +438,8 @@ <string name="fingerprint_dialog_use_fingerprint">Use your fingerprint to continue</string> <!-- Message shown to ask the user to use screenlock to continue.[CHAR LIMIT=NONE] --> <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock">Can\u2019t recognize fingerprint. Use screen lock instead.</string> + <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> + <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string> <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] --> <string name="face_dialog_looking_for_face">Looking for you\u2026</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 23d6ebc46030..0bb5b1ce5179 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -347,13 +347,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms private static final int HAL_ERROR_RETRY_MAX = 20; - private final Runnable mCancelNotReceived = new Runnable() { - @Override - public void run() { - Log.w(TAG, "Cancel not received, transitioning to STOPPED"); - mFingerprintRunningState = mFaceRunningState = BIOMETRIC_STATE_STOPPED; - updateBiometricListeningState(); - } + private final Runnable mFpCancelNotReceived = () -> { + Log.e(TAG, "Fp cancellation not received, transitioning to STOPPED"); + mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + updateFingerprintListeningState(); + }; + + private final Runnable mFaceCancelNotReceived = () -> { + Log.e(TAG, "Face cancellation not received, transitioning to STOPPED"); + mFaceRunningState = BIOMETRIC_STATE_STOPPED; + updateFaceListeningState(); }; private final Handler mHandler; @@ -791,19 +794,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void handleFingerprintError(int msgId, String errString) { Assert.isMainThread(); - if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED && mHandler.hasCallbacks( - mCancelNotReceived)) { - mHandler.removeCallbacks(mCancelNotReceived); + if (mHandler.hasCallbacks(mFpCancelNotReceived)) { + mHandler.removeCallbacks(mFpCancelNotReceived); } + // Error is always the end of authentication lifecycle. + mFingerprintCancelSignal = null; + if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) { setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); updateFingerprintListeningState(); } else { setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); - mFingerprintCancelSignal = null; - mFaceCancelSignal = null; } if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) { @@ -905,6 +908,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void handleFaceAuthFailed() { Assert.isMainThread(); + mFaceCancelSignal = null; setFaceRunningState(BIOMETRIC_STATE_STOPPED); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -983,10 +987,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void handleFaceError(int msgId, String errString) { Assert.isMainThread(); if (DEBUG_FACE) Log.d(TAG, "Face error received: " + errString); - if (msgId == FaceManager.FACE_ERROR_CANCELED && mHandler.hasCallbacks(mCancelNotReceived)) { - mHandler.removeCallbacks(mCancelNotReceived); + if (mHandler.hasCallbacks(mFaceCancelNotReceived)) { + mHandler.removeCallbacks(mFaceCancelNotReceived); } + // Error is always the end of authentication lifecycle + mFaceCancelSignal = null; + if (msgId == FaceManager.FACE_ERROR_CANCELED && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) { setFaceRunningState(BIOMETRIC_STATE_STOPPED); @@ -2368,6 +2375,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void startListeningForFingerprint() { + final int userId = getCurrentUser(); + final boolean unlockPossible = isUnlockWithFingerprintPossible(userId); + if (mFingerprintCancelSignal != null) { + Log.e(TAG, "Cancellation signal is not null, high chance of bug in fp auth lifecycle" + + " management. FP state: " + mFingerprintRunningState + + ", unlockPossible: " + unlockPossible); + } + if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) { setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING); return; @@ -2377,11 +2392,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } if (DEBUG) Log.v(TAG, "startListeningForFingerprint()"); - int userId = getCurrentUser(); - if (isUnlockWithFingerprintPossible(userId)) { - if (mFingerprintCancelSignal != null) { - mFingerprintCancelSignal.cancel(); - } + + if (unlockPossible) { mFingerprintCancelSignal = new CancellationSignal(); if (isEncryptedOrLockdown(userId)) { @@ -2397,6 +2409,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void startListeningForFace() { + final int userId = getCurrentUser(); + final boolean unlockPossible = isUnlockWithFacePossible(userId); + if (mFaceCancelSignal != null) { + Log.e(TAG, "Cancellation signal is not null, high chance of bug in face auth lifecycle" + + " management. Face state: " + mFaceRunningState + + ", unlockPossible: " + unlockPossible); + } + if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) { setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING); return; @@ -2405,11 +2425,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } if (DEBUG) Log.v(TAG, "startListeningForFace(): " + mFaceRunningState); - int userId = getCurrentUser(); - if (isUnlockWithFacePossible(userId)) { - if (mFaceCancelSignal != null) { - mFaceCancelSignal.cancel(); - } + + if (unlockPossible) { mFaceCancelSignal = new CancellationSignal(); // This would need to be updated for multi-sensor devices @@ -2461,9 +2478,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (mFingerprintCancelSignal != null) { mFingerprintCancelSignal.cancel(); mFingerprintCancelSignal = null; - if (!mHandler.hasCallbacks(mCancelNotReceived)) { - mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); - } + mHandler.removeCallbacks(mFpCancelNotReceived); + mHandler.postDelayed(mFpCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); } setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING); } @@ -2478,9 +2494,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (mFaceCancelSignal != null) { mFaceCancelSignal.cancel(); mFaceCancelSignal = null; - if (!mHandler.hasCallbacks(mCancelNotReceived)) { - mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); - } + mHandler.removeCallbacks(mFaceCancelNotReceived); + mHandler.postDelayed(mFaceCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT); } setFaceRunningState(BIOMETRIC_STATE_CANCELLING); } @@ -2842,6 +2857,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mSecureCameraLaunched = false; } + if (mKeyguardBypassController != null) { + // LS visibility has changed, so reset deviceEntryIntent + mKeyguardBypassController.setUserHasDeviceEntryIntent(false); + } + for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index d122610c395d..d46426a03621 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -23,7 +23,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -68,8 +67,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { private float mBurnInProgress; private float mInterpolatedDarkAmount; - private ValueAnimator mHintAnimator; - public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mFingerprintDrawable = new UdfpsFpDrawable(context); @@ -94,9 +91,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { new KeyPath("**"), LottieProperty.COLOR_FILTER, frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP) ); - - mHintAnimator = ObjectAnimator.ofFloat(mLockScreenFp, "progress", 1f, 0f, 1f); - mHintAnimator.setDuration(4000); } @Override @@ -183,19 +177,11 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { } void onDozeAmountChanged(float linear, float eased) { - mHintAnimator.cancel(); mInterpolatedDarkAmount = eased; updateAlpha(); updateBurnInOffsets(); } - void animateHint() { - if (!isShadeLocked() && !mUdfpsRequested && mAlpha == 255 - && mLockScreenFp.isVisibleToUser()) { - mHintAnimator.start(); - } - } - /** * Animates in the bg protection circle behind the fp icon to highlight the icon. */ diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 58f1254da563..4896305daa2e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -20,14 +20,10 @@ 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; -import androidx.annotation.Nullable; - import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -46,14 +42,8 @@ import java.io.PrintWriter; /** * Class that coordinates non-HBM animations during keyguard authentication. - * - * Highlights the udfps icon when: - * - Face authentication has failed - * - Face authentication has been run for > 2 seconds */ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { - private static final long AFTER_FACE_AUTH_HINT_DELAY = 2000; - @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final DelayableExecutor mExecutor; @@ -62,12 +52,10 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull private final ConfigurationController mConfigurationController; @NonNull private final UdfpsController mUdfpsController; - @Nullable private Runnable mCancelDelayedHintRunnable; private boolean mShowingUdfpsBouncer; private boolean mUdfpsRequested; private boolean mQsExpanded; private boolean mFaceDetectRunning; - private boolean mHintShown; private int mStatusBarState; private float mTransitionToFullShadeProgress; private float mLastDozeAmount; @@ -110,10 +98,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override protected void onViewAttached() { super.onViewAttached(); - mHintShown = false; - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); - updateFaceDetectRunning(mKeyguardUpdateMonitor.isFaceDetectionRunning()); - final float dozeAmount = mStatusBarStateController.getDozeAmount(); mLastDozeAmount = dozeAmount; mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); @@ -136,7 +120,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override protected void onViewDetached() { super.onViewDetached(); - mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); mFaceDetectRunning = false; mStatusBarStateController.removeCallback(mStateListener); @@ -146,11 +129,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) { mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null); } - - if (mCancelDelayedHintRunnable != null) { - mCancelDelayedHintRunnable.run(); - mCancelDelayedHintRunnable = null; - } } @Override @@ -249,36 +227,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } } - private void cancelDelayedHint() { - if (mCancelDelayedHintRunnable != null) { - mCancelDelayedHintRunnable.run(); - mCancelDelayedHintRunnable = null; - } - } - - private void updateFaceDetectRunning(boolean running) { - if (mFaceDetectRunning == running) { - return; - } - - // show udfps hint a few seconds after face auth started running - if (!mFaceDetectRunning && running && !mHintShown && mCancelDelayedHintRunnable == null) { - // Face detect started running, show udfps hint after a delay - mCancelDelayedHintRunnable = mExecutor.executeDelayed(() -> showHint(false), - AFTER_FACE_AUTH_HINT_DELAY); - } - - mFaceDetectRunning = running; - } - - private void showHint(boolean forceShow) { - cancelDelayedHint(); - if (!mHintShown || forceShow) { - mHintShown = true; - mView.animateHint(); - } - } - /** * Set the progress we're currently transitioning to the full shade. 0.0f means we're not * transitioning yet, while 1.0f means we've fully dragged down. @@ -318,39 +266,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } }; - private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = - new KeyguardUpdateMonitorCallback() { - public void onBiometricRunningStateChanged(boolean running, - BiometricSourceType biometricSourceType) { - if (biometricSourceType == BiometricSourceType.FACE) { - updateFaceDetectRunning(running); - } - } - - public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { - if (biometricSourceType == BiometricSourceType.FACE) { - // show udfps hint when face auth fails - showHint(true); - } - } - - public void onBiometricError(int msgId, String errString, - BiometricSourceType biometricSourceType) { - if (biometricSourceType == BiometricSourceType.FACE) { - // show udfps hint when face auth fails - showHint(true); - } - } - - public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType, boolean isStrongBiometric) { - if (biometricSourceType == BiometricSourceType.FACE) { - // cancel delayed hint if face auth succeeded - cancelDelayedHint(); - } - } - }; - private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor = new StatusBarKeyguardViewManager.AlternateAuthInterceptor() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 23c44131ab60..b2db86f16104 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -115,6 +115,7 @@ public class DozeSensors { mSecureSettings = secureSettings; mCallback = callback; mProximitySensor = proximitySensor; + mProximitySensor.setTag(TAG); mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx(); mListeningProxSensors = !mSelectivelyRegisterProxSensors; mScreenOffUdfpsEnabled = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 84cb12cf5e49..ee3d40edc2eb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2603,7 +2603,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (mContext.getResources().getBoolean( com.android.internal.R.bool.config_guestUserAutoCreated)) { // TODO(b/191067027): Move post-boot guest creation to system_server - mUserSwitcherController.guaranteeGuestPresent(); + mUserSwitcherController.schedulePostBootGuestCreation(); } mBootCompleted = true; adjustStatusBarLocked(false, true); @@ -2759,7 +2759,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // Don't hide the keyguard due to a doze change if there's a lock pending, because we're // just going to show it again. - if (mShowing || !mPendingLock) { + // If the device is not capable of controlling the screen off animation, SysUI needs to + // update lock screen state in ATMS here, otherwise ATMS tries to resume activities when + // enabling doze state. + if (mShowing || !mPendingLock || !mDozeParameters.canControlUnlockedScreenOff()) { setShowingLocked(mShowing); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index ce1066ee41c2..1bd36644bbc5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -268,7 +268,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } @Override - public void setExpansion(float expansion) { + public void setExpansion(float expansion, float proposedTranslation) { mLastExpansion = expansion; updateSelected(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index c9230d684b1b..1c841eca65f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -498,11 +498,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca : headerTranslation); } int currentHeight = getView().getHeight(); - mLastHeaderTranslation = headerTranslation; - if (expansion == mLastQSExpansion && mLastKeyguardAndExpanded == onKeyguardAndExpanded - && mLastViewHeight == currentHeight) { + if (expansion == mLastQSExpansion + && mLastKeyguardAndExpanded == onKeyguardAndExpanded + && mLastViewHeight == currentHeight + && mLastHeaderTranslation == headerTranslation) { return; } + mLastHeaderTranslation = headerTranslation; mLastQSExpansion = expansion; mLastKeyguardAndExpanded = onKeyguardAndExpanded; mLastViewHeight = currentHeight; @@ -522,8 +524,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); - mQSPanelController.getTileLayout().setExpansion(expansion); - mQuickQSPanelController.getTileLayout().setExpansion(expansion); + mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); + mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff); if (fullyCollapsed) { mQSPanelScrollView.setScrollY(0); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 1b4ee734a491..cde80e66ba26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -742,7 +742,7 @@ public class QSPanel extends LinearLayout implements Tunable { void setListening(boolean listening, UiEventLogger uiEventLogger); /** - * Set the minimum number of rows to show + * Sets the minimum number of rows to show * * @param minRows the minimum. */ @@ -751,7 +751,7 @@ public class QSPanel extends LinearLayout implements Tunable { } /** - * Set the max number of columns to show + * Sets the max number of columns to show * * @param maxColumns the maximum * @@ -761,7 +761,10 @@ public class QSPanel extends LinearLayout implements Tunable { return false; } - default void setExpansion(float expansion) {} + /** + * Sets the expansion value and proposedTranslation to panel. + */ + default void setExpansion(float expansion, float proposedTranslation) {} int getNumVisibleTiles(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 985943bc8b05..c5bfe97403e7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -177,6 +177,8 @@ public class QuickQSPanel extends QSPanel { static class QQSSideLabelTileLayout extends SideLabelTileLayout { + private boolean mLastSelected; + QQSSideLabelTileLayout(Context context) { super(context, null); setClipChildren(false); @@ -222,5 +224,29 @@ public class QuickQSPanel extends QSPanel { } } } + + @Override + public void setExpansion(float expansion, float proposedTranslation) { + if (expansion > 0f && expansion < 1f) { + return; + } + // The cases we must set select for marquee when QQS/QS collapsed, and QS full expanded. + // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this + // point we want them to be selected so the tiles will marquee (but not at other points + // of expansion. + boolean selected = (expansion == 1f || proposedTranslation < 0f); + if (mLastSelected == selected) { + return; + } + // We set it as not important while we change this, so setting each tile as selected + // will not cause them to announce themselves until the user has actually selected the + // item. + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setSelected(selected); + } + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + mLastSelected = selected; + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 70685a68e182..222539d49526 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -195,9 +195,6 @@ open class QSTileViewImpl @JvmOverloads constructor( // sibling methods to have special behavior for labelContainer. labelContainer.forceUnspecifiedMeasure = true secondaryLabel.alpha = 0f - // Do not marque in QQS - label.ellipsize = TextUtils.TruncateAt.END - secondaryLabel.ellipsize = TextUtils.TruncateAt.END } setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE)) setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 3890c1aa4e4f..503b5c0ee4b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -825,7 +825,11 @@ public class KeyguardIndicationController { 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); + if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) { + showTransientIndication(R.string.keyguard_unlock_press); + } else { + showTransientIndication(R.string.keyguard_face_failed_use_fp); + } } else { showTransientIndication(R.string.keyguard_try_fingerprint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 1b4ae9ea3f32..2c810c93b2ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -157,15 +157,13 @@ public class StackScrollAlgorithm { // After the shelf has updated its yTranslation, explicitly set alpha=0 for view below shelf // to skip rendering them in the hardware layer. We do not set them invisible because that // runs invalidate & onDraw when these views return onscreen, which is more expensive. + if (shelf.getViewState().hidden) { + // When the shelf is hidden, it won't clip views, so we don't hide rows + return; + } final float shelfTop = shelf.getViewState().yTranslation; for (ExpandableView view : algorithmState.visibleChildren) { - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - if (row.isHeadsUp() || row.isHeadsUpAnimatingAway()) { - continue; - } - } final float viewTop = view.getViewState().yTranslation; if (viewTop >= shelfTop) { view.getViewState().alpha = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 68024726c5de..5a6db213d87f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -231,7 +231,9 @@ public class DozeParameters implements TunerService.Tunable, * possible if AOD isn't even enabled or if the flag is disabled. */ public boolean canControlUnlockedScreenOff() { - return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations(); + return getAlwaysOn() + && mFeatureFlags.useNewLockscreenAnimations() + && !getDisplayNeedsBlanking(); } private boolean getBoolean(String propName, int resId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 8f1a5782e779..7548d1c435ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -26,6 +26,7 @@ import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; import android.app.IActivityTaskManager; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -114,6 +115,8 @@ public class UserSwitcherController implements Dumpable { @VisibleForTesting final GuestResumeSessionReceiver mGuestResumeSessionReceiver; private final KeyguardStateController mKeyguardStateController; + private final DeviceProvisionedController mDeviceProvisionedController; + private final DevicePolicyManager mDevicePolicyManager; protected final Handler mHandler; private final ActivityStarter mActivityStarter; private final BroadcastDispatcher mBroadcastDispatcher; @@ -149,6 +152,8 @@ public class UserSwitcherController implements Dumpable { UserManager userManager, UserTracker userTracker, KeyguardStateController keyguardStateController, + DeviceProvisionedController deviceProvisionedController, + DevicePolicyManager devicePolicyManager, @Main Handler handler, ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher, @@ -178,6 +183,8 @@ public class UserSwitcherController implements Dumpable { mGuestIsResetting = new AtomicBoolean(); mGuestCreationScheduled = new AtomicBoolean(); mKeyguardStateController = keyguardStateController; + mDeviceProvisionedController = deviceProvisionedController; + mDevicePolicyManager = devicePolicyManager; mHandler = handler; mActivityStarter = activityStarter; mUserManager = userManager; @@ -733,10 +740,27 @@ public class UserSwitcherController implements Dumpable { } /** + * Guarantee guest is present only if the device is provisioned. Otherwise, create a content + * observer to wait until the device is provisioned, then schedule the guest creation. + */ + public void schedulePostBootGuestCreation() { + if (isDeviceAllowedToAddGuest()) { + guaranteeGuestPresent(); + } else { + mDeviceProvisionedController.addCallback(mGuaranteeGuestPresentAfterProvisioned); + } + } + + private boolean isDeviceAllowedToAddGuest() { + return mDeviceProvisionedController.isDeviceProvisioned() + && !mDevicePolicyManager.isDeviceManaged(); + } + + /** * If there is no guest on the device, schedule creation of a new guest user in the background. */ - public void guaranteeGuestPresent() { - if (mUserManager.findCurrentGuestUser() == null) { + private void guaranteeGuestPresent() { + if (isDeviceAllowedToAddGuest() && mUserManager.findCurrentGuestUser() == null) { scheduleGuestCreation(); } } @@ -1056,6 +1080,21 @@ public class UserSwitcherController implements Dumpable { } }; + private final DeviceProvisionedController.DeviceProvisionedListener + mGuaranteeGuestPresentAfterProvisioned = + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + if (isDeviceAllowedToAddGuest()) { + mBgExecutor.execute( + () -> mDeviceProvisionedController.removeCallback( + mGuaranteeGuestPresentAfterProvisioned)); + guaranteeGuestPresent(); + } + } + }; + + private final class ExitGuestDialog extends SystemUIDialog implements DialogInterface.OnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index b38fc77fd131..90e022a52d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -290,15 +290,18 @@ public class ProximitySensor implements ThresholdSensor { return; } - if (!mSecondaryThresholdSensor.isLoaded()) { + + if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary logDebug("Primary sensor event: " + event.getBelow() + ". No secondary."); onSensorEvent(event); - } else if (event.getBelow()) { + } else if (event.getBelow()) { // Covered? Check secondary. logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary."); if (mCancelSecondaryRunnable != null) { mCancelSecondaryRunnable.run(); } mSecondaryThresholdSensor.resume(); + } else { // Uncovered. Report immediately. + onSensorEvent(event); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 3320852ca1f4..60b92ef45291 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -2248,6 +2248,11 @@ public class VolumeDialogImpl implements VolumeDialog, @Override public void onClick(View view) { + // If the ringer drawer isn't open, don't let anything in it be clicked. + if (!mIsRingerDrawerOpen) { + return; + } + setRingerMode(mClickedRingerMode); mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index ace2c71d9c63..3431a9d895d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.IActivityTaskManager +import android.app.admin.DevicePolicyManager import android.content.Context import android.content.DialogInterface import android.content.Intent @@ -63,6 +64,8 @@ import org.mockito.MockitoAnnotations @SmallTest class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController + @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Mock private lateinit var handler: Handler @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var userManager: UserManager @@ -107,6 +110,8 @@ class UserSwitcherControllerTest : SysuiTestCase() { userManager, userTracker, keyguardStateController, + deviceProvisionedController, + devicePolicyManager, handler, activityStarter, broadcastDispatcher, diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java index cc2afe2f7b8a..a34c5986f36c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java @@ -18,6 +18,7 @@ package com.android.systemui.util.sensors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -60,7 +61,8 @@ public class ProximitySensorDualTest extends SysuiTestCase { } @Test - public void testPrimaryBelowDoesNotInvokeSecondary() { + public void testInitiallyAbovePrimary() { + TestableListener listener = new TestableListener(); mProximitySensor.register(listener); @@ -70,12 +72,71 @@ public class ProximitySensorDualTest extends SysuiTestCase { assertNull(listener.mLastEvent); assertEquals(0, listener.mCallCount); - // Trigger primary sensor. Our secondary sensor is not registered. mThresholdSensorPrimary.triggerEvent(false, 0); + assertNotNull(listener.mLastEvent); + assertFalse(listener.mLastEvent.getBelow()); + assertEquals(1, listener.mCallCount); + } + + @Test + public void testInitiallyBelowPrimaryAboveSecondary() { + + TestableListener listener = new TestableListener(); + + mProximitySensor.register(listener); + assertTrue(mProximitySensor.isRegistered()); assertFalse(mThresholdSensorPrimary.isPaused()); assertTrue(mThresholdSensorSecondary.isPaused()); assertNull(listener.mLastEvent); assertEquals(0, listener.mCallCount); + + mThresholdSensorPrimary.triggerEvent(true, 0); + assertNull(listener.mLastEvent); + assertEquals(0, listener.mCallCount); + + mThresholdSensorSecondary.triggerEvent(false, 1); + assertNotNull(listener.mLastEvent); + assertFalse(listener.mLastEvent.getBelow()); + assertEquals(1, listener.mCallCount); + } + + @Test + public void testInitiallyBelowPrimaryAndSecondary() { + + TestableListener listener = new TestableListener(); + + mProximitySensor.register(listener); + assertTrue(mProximitySensor.isRegistered()); + assertFalse(mThresholdSensorPrimary.isPaused()); + assertTrue(mThresholdSensorSecondary.isPaused()); + assertNull(listener.mLastEvent); + assertEquals(0, listener.mCallCount); + + mThresholdSensorPrimary.triggerEvent(true, 0); + assertNull(listener.mLastEvent); + assertEquals(0, listener.mCallCount); + + mThresholdSensorSecondary.triggerEvent(true, 1); + assertNotNull(listener.mLastEvent); + assertTrue(listener.mLastEvent.getBelow()); + assertEquals(1, listener.mCallCount); + } + + @Test + public void testPrimaryBelowDoesNotInvokeSecondary() { + TestableListener listener = new TestableListener(); + + mProximitySensor.register(listener); + assertTrue(mProximitySensor.isRegistered()); + assertFalse(mThresholdSensorPrimary.isPaused()); + assertTrue(mThresholdSensorSecondary.isPaused()); + assertNull(listener.mLastEvent); + assertEquals(0, listener.mCallCount); + + // Trigger primary sensor. Our secondary sensor is not registered. + mThresholdSensorPrimary.triggerEvent(false, 0); + assertFalse(mThresholdSensorPrimary.isPaused()); + assertTrue(mThresholdSensorSecondary.isPaused()); } @Test diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index cd332a68fa88..4946ad442b0a 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -125,7 +125,7 @@ public class AppPredictionPerUserService extends // connect with remote AppPredictionService instead for dark launch usesPeopleService = false; } - final boolean serviceExists = resolveService(sessionId, false, + final boolean serviceExists = resolveService(sessionId, true, usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId)); if (serviceExists && !mSessionInfos.containsKey(sessionId)) { final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo( diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index a56b1db1494c..5aec6aa99c12 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -3285,57 +3285,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - 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. @@ -4926,14 +4875,5 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 07847f1df053..45e6f1ec4bf8 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -102,6 +102,7 @@ import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; +import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.ComponentName.WithComponentName; import android.content.Context; @@ -307,6 +308,7 @@ public final class ActiveServices { */ @ChangeId @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + @Overridable static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L; /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e0df4b797fe5..99ae52c00995 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -193,7 +193,6 @@ import android.app.usage.UsageEvents.Event; 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; @@ -16598,13 +16597,6 @@ public class ActivityManagerService extends IActivityManager.Stub 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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a9905dcf8904..64b9bd98a2fc 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -45,6 +45,9 @@ import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; +import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED; +import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED; +import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED; import static android.app.AppOpsManager.OpEventProxyInfo; import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING; @@ -1238,6 +1241,11 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, true, event.getAttributionFlags(), event.getAttributionChainId()); } + // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND + // TODO ntmyren: figure out how to get the real mode. + scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName, + tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED, + event.getAttributionFlags(), event.getAttributionChainId()); } mPausedInProgressEvents = null; } @@ -3438,7 +3446,7 @@ public class AppOpsService extends IAppOpsService.Stub { + " package " + packageName + "flags: " + AppOpsManager.flagsToString(flags)); return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, - packageName + " flags: " + AppOpsManager.flagsToString(flags)); + packageName); } final Op op = getOpLocked(ops, code, uid, true); final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag); @@ -3945,13 +3953,15 @@ public class AppOpsService extends IAppOpsService.Stub { } boolean isRestricted = false; + int startType = START_TYPE_FAILED; synchronized (this) { final Ops ops = getOpsLocked(uid, packageName, attributionTag, pvr.isAttributionTagValid, pvr.bypass, /* edit */ true); if (ops == null) { if (!dryRun) { scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, - flags, AppOpsManager.MODE_IGNORED); + flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags, + attributionChainId); } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + packageName + " flags: " @@ -3977,7 +3987,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, - flags, uidMode); + flags, uidMode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(uidMode, code, attributionTag, packageName); } @@ -3993,7 +4003,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, - flags, mode); + flags, mode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(mode, code, attributionTag, packageName); } @@ -4011,12 +4021,14 @@ public class AppOpsService extends IAppOpsService.Stub { attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, flags, attributionFlags, attributionChainId); + startType = START_TYPE_STARTED; } } catch (RemoteException e) { throw new RuntimeException(e); } scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, - isRestricted ? MODE_IGNORED : MODE_ALLOWED); + isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags, + attributionChainId); } } @@ -4187,7 +4199,9 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, - String attributionTag, @OpFlags int flags, @Mode int result) { + String attributionTag, @OpFlags int flags, @Mode int result, + @AppOpsManager.OnOpStartedListener.StartedType int startedType, + @AttributionFlags int attributionFlags, int attributionChainId) { ArraySet<StartedCallback> dispatchedCallbacks = null; final int callbackListCount = mStartedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { @@ -4213,12 +4227,13 @@ public class AppOpsService extends IAppOpsService.Stub { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpStarted, this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags, - result)); + result, startedType, attributionFlags, attributionChainId)); } private void notifyOpStarted(ArraySet<StartedCallback> callbacks, int code, int uid, String packageName, String attributionTag, @OpFlags int flags, - @Mode int result) { + @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType, + @AttributionFlags int attributionFlags, int attributionChainId) { final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = callbacks.size(); @@ -4226,7 +4241,7 @@ public class AppOpsService extends IAppOpsService.Stub { final StartedCallback callback = callbacks.valueAt(i); try { callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags, - result); + result, startedType, attributionFlags, attributionChainId); } catch (RemoteException e) { /* do nothing */ } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f7d091498456..34e2578f7855 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -736,6 +736,11 @@ public class AudioService extends IAudioService.Stub private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT; private long mLoweredFromNormalToVibrateTime; + // Uid of the active hotword detection service to check if caller is the one or not. + @GuardedBy("mHotwordDetectionServiceUidLock") + private int mHotwordDetectionServiceUid = android.os.Process.INVALID_UID; + private final Object mHotwordDetectionServiceUidLock = new Object(); + // Array of Uids of valid accessibility services to check if caller is one of them private final Object mAccessibilityServiceUidsLock = new Object(); @GuardedBy("mAccessibilityServiceUidsLock") @@ -1337,6 +1342,9 @@ public class AudioService extends IAudioService.Stub updateAssistantUId(true); AudioSystem.setRttEnabled(mRttEnabled); } + synchronized (mHotwordDetectionServiceUidLock) { + AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid); + } synchronized (mAccessibilityServiceUidsLock) { AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } @@ -9108,6 +9116,16 @@ public class AudioService extends IAudioService.Stub } @Override + public void setHotwordDetectionServiceUid(int uid) { + synchronized (mHotwordDetectionServiceUidLock) { + if (mHotwordDetectionServiceUid != uid) { + mHotwordDetectionServiceUid = uid; + AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid); + } + } + } + + @Override public void setAccessibilityServiceUids(IntArray uids) { synchronized (mAccessibilityServiceUidsLock) { if (uids.size() == 0) { diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 973fbd2bba17..6d567807f357 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -367,6 +367,14 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { } /** + * Same as {@link AudioSystem#setHotwordDetectionServiceUid(int)} + * Communicate UID of current HotwordDetectionService to audio policy service. + */ + public int setHotwordDetectionServiceUid(int uid) { + return AudioSystem.setHotwordDetectionServiceUid(uid); + } + + /** * Same as {@link AudioSystem#setCurrentImeUid(int)} * Communicate UID of current InputMethodService to audio policy service. */ 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 f4327e8a104d..6f38ed04cd96 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -82,7 +82,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private long mStartTimeMs; - protected boolean mAuthAttempted; + private boolean mAuthAttempted; // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update // the state. We should think of a way to improve this in the future. @@ -98,6 +98,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> */ protected abstract void handleLifecycleAfterAuth(boolean authenticated); + /** + * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched. + * etc) + */ + public abstract boolean wasUserDetected(); + public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, @@ -180,7 +186,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> + ", isBP: " + isBiometricPrompt() + ", listener: " + listener + ", requireConfirmation: " + mRequireConfirmation - + ", user: " + getTargetUserId()); + + ", user: " + getTargetUserId() + + ", clientMonitor: " + toString()); final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId()); if (isCryptoOperation()) { @@ -304,6 +311,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public void handleLifecycleAfterAuth() { AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */); } + + @Override + public void sendAuthenticationCanceled() { + sendCancelOnly(listener); + } }); } else { // Allow system-defined limit of number of attempts before giving up @@ -338,10 +350,30 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public void handleLifecycleAfterAuth() { AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */); } + + @Override + public void sendAuthenticationCanceled() { + sendCancelOnly(listener); + } }); } } + private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) { + if (listener == null) { + Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null"); + return; + } + try { + listener.onError(getSensorId(), + getCookie(), + BiometricConstants.BIOMETRIC_ERROR_CANCELED, + 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + @Override public void onAcquired(int acquiredInfo, int vendorCode) { super.onAcquired(acquiredInfo, vendorCode); @@ -355,9 +387,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } @Override - public void onError(int errorCode, int vendorCode) { + public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) { super.onError(errorCode, vendorCode); mState = STATE_STOPPED; + + CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError); } /** @@ -419,4 +453,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public boolean interruptsPrecedingClients() { return true; } + + public boolean wasAuthAttempted() { + return mAuthAttempted; + } } 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 a15ecada3cae..25d4a38cd475 100644 --- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java @@ -22,6 +22,7 @@ import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTyp import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.biometrics.BiometricConstants; import android.os.Handler; import android.os.Looper; import android.util.Slog; @@ -54,7 +55,7 @@ public class CoexCoordinator { /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business - * logic. + * logic for accept and reject. */ interface Callback { /** @@ -73,6 +74,22 @@ public class CoexCoordinator { * from scheduler if auth was successful). */ void handleLifecycleAfterAuth(); + + /** + * Requests the owner to notify the caller that authentication was canceled. + */ + void sendAuthenticationCanceled(); + } + + /** + * Callback interface notifying the owner of "results" from the CoexCoordinator's business + * logic for errors. + */ + interface ErrorCallback { + /** + * Requests the owner to initiate a vibration for this event. + */ + void sendHapticFeedback(); } private static CoexCoordinator sInstance; @@ -198,6 +215,9 @@ public class CoexCoordinator { mClientMap.remove(sensorType); } + /** + * Notify the coordinator that authentication succeeded (accepted) + */ public void onAuthenticationSucceeded(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @NonNull Callback callback) { @@ -268,6 +288,9 @@ public class CoexCoordinator { } } + /** + * Notify the coordinator that a rejection has occurred. + */ public void onAuthenticationRejected(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @LockoutTracker.LockoutMode int lockoutMode, @@ -352,11 +375,63 @@ public class CoexCoordinator { } } + /** + * Notify the coordinator that an error has occurred. + */ + public void onAuthenticationError(@NonNull AuthenticationClient<?> client, + @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) { + // Figure out non-coex state + final boolean shouldUsuallyVibrate; + if (isCurrentFaceAuth(client)) { + final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected(); + final boolean authAttempted = client.wasAuthAttempted(); + + switch (error) { + case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: + shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard; + break; + default: + shouldUsuallyVibrate = false; + break; + } + } else { + shouldUsuallyVibrate = false; + } + + // Figure out coex state + final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard(); + final boolean hapticSuppressedByCoex; + + if (keyguardAdvancedLogic) { + if (isSingleAuthOnly(client)) { + hapticSuppressedByCoex = false; + } else { + hapticSuppressedByCoex = isCurrentFaceAuth(client) + && !client.isKeyguardBypassEnabled(); + } + } else { + hapticSuppressedByCoex = false; + } + + // Combine and send feedback if appropriate + Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate + + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex); + if (shouldUsuallyVibrate && !hapticSuppressedByCoex) { + callback.sendHapticFeedback(); + } + } + @Nullable private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) { for (SuccessfulAuth auth : mSuccessfulAuths) { if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) { - Slog.d(TAG, "Removing stale auth: " + auth); + // TODO(b/193089985): This removes the auth but does not notify the client with + // an appropriate lifecycle event (such as ERROR_CANCELED), and violates the + // API contract. However, this might be OK for now since the validity duration + // is way longer than the time it takes to auth with fingerprint. + Slog.e(TAG, "Removing stale auth: " + auth); mSuccessfulAuths.remove(auth); } else if (auth.mSensorType == SENSOR_TYPE_FACE) { mSuccessfulAuths.remove(auth); @@ -367,9 +442,13 @@ public class CoexCoordinator { } private void removeAndFinishAllFaceFromQueue() { + // Note that these auth are all successful, but have never notified the client (e.g. + // keyguard). To comply with the authentication lifecycle, we must notify the client that + // auth is "done". The safest thing to do is to send ERROR_CANCELED. for (SuccessfulAuth auth : mSuccessfulAuths) { if (auth.mSensorType == SENSOR_TYPE_FACE) { - Slog.d(TAG, "Removing from queue and finishing: " + auth); + Slog.d(TAG, "Removing from queue, canceling, and finishing: " + auth); + auth.mCallback.sendAuthenticationCanceled(); auth.mCallback.handleLifecycleAfterAuth(); mSuccessfulAuths.remove(auth); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index f7fd8d0972f6..d66a27920f49 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -127,7 +127,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } } - private boolean wasUserDetected() { + @Override + public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED @@ -160,7 +161,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } @Override - public void onError(int error, int vendorCode) { + public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, @@ -169,25 +170,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements vendorCode, getTargetUserId())); - switch (error) { - case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: - if (!wasUserDetected() && !isBiometricPrompt()) { - // No vibration if user was not detected on keyguard - break; - } - case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: - case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: - if (mAuthAttempted) { - // Only vibrate if auth was attempted. If the user was already locked out prior - // to starting authentication, do not vibrate. - vibrateError(); - } - break; - case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL: - BiometricNotificationUtils.showReEnrollmentNotification(getContext()); - break; - default: - break; + if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) { + BiometricNotificationUtils.showReEnrollmentNotification(getContext()); } super.onError(error, vendorCode); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index c33b957223a4..33950af2216f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -115,7 +115,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } } - private boolean wasUserDetected() { + @Override + public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED @@ -147,7 +148,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } @Override - public void onError(int error, int vendorCode) { + public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, @@ -156,24 +157,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { vendorCode, getTargetUserId())); - switch (error) { - case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: - if (!wasUserDetected() && !isBiometricPrompt()) { - // No vibration if user was not detected on keyguard - break; - } - case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: - case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: - if (mAuthAttempted) { - // Only vibrate if auth was attempted. If the user was already locked out prior - // to starting authentication, do not vibrate. - vibrateError(); - } - break; - default: - break; - } - super.onError(error, vendorCode); } 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 8835c1e02610..37ee76adeece 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 @@ -106,6 +106,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } @Override + public boolean wasUserDetected() { + // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout + return false; + } + + @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { super.onAuthenticated(identifier, authenticated, token); 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 83f1480f4611..5060744bb33e 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 @@ -153,6 +153,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi } @Override + public boolean wasUserDetected() { + // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout + return false; + } + + @Override public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) { mLockoutFrameworkImpl.addFailedAttemptForUser(userId); return super.handleFailedAttempt(userId); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index f23ae6e2340c..186986e17c14 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -153,7 +153,7 @@ public class DisplayModeDirector { updateVoteLocked(displayId, priority, vote); } }; - mSensorObserver = new SensorObserver(context, ballotBox); + mSensorObserver = new SensorObserver(context, ballotBox, injector); mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler()); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); mDeviceConfig = injector.getDeviceConfig(); @@ -2127,27 +2127,34 @@ public class DisplayModeDirector { } } - private static class SensorObserver implements ProximityActiveListener { - private static final String PROXIMITY_SENSOR_NAME = null; - private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY; + private final class SensorObserver implements ProximityActiveListener, + DisplayManager.DisplayListener { + private final String mProximitySensorName = null; + private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY; private final BallotBox mBallotBox; private final Context mContext; + private final Injector mInjector; private DisplayManager mDisplayManager; private DisplayManagerInternal mDisplayManagerInternal; private boolean mIsProxActive = false; + private final SparseBooleanArray mDozeStateByDisplay; - SensorObserver(Context context, BallotBox ballotBox) { + SensorObserver(Context context, BallotBox ballotBox, Injector injector) { mContext = context; mBallotBox = ballotBox; + mInjector = injector; + mDozeStateByDisplay = new SparseBooleanArray(); } @Override public void onProximityActive(boolean isActive) { - if (mIsProxActive != isActive) { - mIsProxActive = isActive; - recalculateVotes(); + synchronized (mLock) { + if (mIsProxActive != isActive) { + mIsProxActive = isActive; + recalculateVotesLocked(); + } } } @@ -2158,17 +2165,27 @@ public class DisplayModeDirector { final SensorManagerInternal sensorManager = LocalServices.getService(SensorManagerInternal.class); sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this); + + synchronized (mLock) { + for (Display d : mDisplayManager.getDisplays()) { + mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d)); + } + } + mInjector.registerDisplayListener(this, BackgroundThread.getHandler(), + DisplayManager.EVENT_FLAG_DISPLAY_ADDED + | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED); } - private void recalculateVotes() { + private void recalculateVotesLocked() { final Display[] displays = mDisplayManager.getDisplays(); for (Display d : displays) { int displayId = d.getDisplayId(); Vote vote = null; - if (mIsProxActive) { + if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) { final RefreshRateRange rate = mDisplayManagerInternal.getRefreshRateForDisplayAndSensor( - displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE); + displayId, mProximitySensorName, mProximitySensorType); if (rate != null) { vote = Vote.forRefreshRates(rate.min, rate.max); } @@ -2180,6 +2197,41 @@ public class DisplayModeDirector { void dumpLocked(PrintWriter pw) { pw.println(" SensorObserver"); pw.println(" mIsProxActive=" + mIsProxActive); + pw.println(" mDozeStateByDisplay:"); + for (int i = 0; i < mDozeStateByDisplay.size(); i++) { + final int id = mDozeStateByDisplay.keyAt(i); + final boolean dozed = mDozeStateByDisplay.valueAt(i); + pw.println(" " + id + " -> " + dozed); + } + } + + @Override + public void onDisplayAdded(int displayId) { + boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId)); + synchronized (mLock) { + mDozeStateByDisplay.put(displayId, isDozeState); + recalculateVotesLocked(); + } + } + + @Override + public void onDisplayChanged(int displayId) { + boolean wasDozeState = mDozeStateByDisplay.get(displayId); + synchronized (mLock) { + mDozeStateByDisplay.put(displayId, + mInjector.isDozeState(mDisplayManager.getDisplay(displayId))); + if (wasDozeState != mDozeStateByDisplay.get(displayId)) { + recalculateVotesLocked(); + } + } + } + + @Override + public void onDisplayRemoved(int displayId) { + synchronized (mLock) { + mDozeStateByDisplay.delete(displayId); + recalculateVotesLocked(); + } } } @@ -2411,6 +2463,8 @@ public class DisplayModeDirector { Handler handler, long flags); BrightnessInfo getBrightnessInfo(int displayId); + + boolean isDozeState(Display d); } @VisibleForTesting @@ -2463,6 +2517,11 @@ public class DisplayModeDirector { return null; } + @Override + public boolean isDozeState(Display d) { + return Display.isDozeState(d.getState()); + } + private DisplayManager getDisplayManager() { if (mDisplayManager == null) { mDisplayManager = mContext.getSystemService(DisplayManager.class); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 06ff69176bb7..ed9b539c05df 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -1287,20 +1287,6 @@ public class AppsFilter implements Watchable, Snappable { } } - // This package isn't technically installed and won't be written to settings, so we can - // treat it as filtered until it's available again. - final AndroidPackage targetPkg = targetPkgSetting.pkg; - if (targetPkg == null) { - if (DEBUG_LOGGING) { - Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); - } - return true; - } - if (targetPkg.isStaticSharedLibrary()) { - // not an app, this filtering takes place at a higher level - return false; - } - final String targetName = targetPkg.getPackageName(); if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId"); } @@ -1343,6 +1329,21 @@ public class AppsFilter implements Watchable, Snappable { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } + + // This package isn't technically installed and won't be written to settings, so we can + // treat it as filtered until it's available again. + final AndroidPackage targetPkg = targetPkgSetting.pkg; + if (targetPkg == null) { + if (DEBUG_LOGGING) { + Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null"); + } + return true; + } + if (targetPkg.isStaticSharedLibrary()) { + // not an app, this filtering takes place at a higher level + return false; + } + try { if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); @@ -1415,6 +1416,7 @@ public class AppsFilter implements Watchable, Snappable { if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); } + final String targetName = targetPkg.getPackageName(); if (callingSharedPkgSettings != null) { int size = callingSharedPkgSettings.size(); for (int index = 0; index < size; index++) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 031910a81260..904a1f0d6f4f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -26493,7 +26493,7 @@ public class PackageManagerService extends IPackageManager.Stub } boolean readPermissionStateForUser(@UserIdInt int userId) { - synchronized (mPackages) { + synchronized (mLock) { mPermissionManager.writeLegacyPermissionStateTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); mPermissionManager.readLegacyPermissionStateTEMP(); @@ -26569,7 +26569,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null || alias == null) { return null; } - synchronized(mLock) { + synchronized (mLock) { final AndroidPackage pkg = mPackages.get(packageName); if (pkg == null || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), @@ -26616,7 +26616,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null || ks == null) { return false; } - synchronized(mLock) { + synchronized (mLock) { final AndroidPackage pkg = mPackages.get(packageName); if (pkg == null || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index bf82bd8b09d5..88dd03333262 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -176,6 +176,10 @@ public abstract class PackageSettingBase extends SettingBase { name = orig.name; realName = orig.realName; doCopy(orig); + // Clone the user states. + for (int i = 0; i < mUserState.size(); i++) { + mUserState.put(mUserState.keyAt(i), new PackageUserState(mUserState.valueAt(i))); + } } public void setInstallerPackageName(String packageName) { @@ -314,6 +318,7 @@ public abstract class PackageSettingBase extends SettingBase { void setInstalled(boolean inst, int userId) { modifyUserState(userId).installed = inst; + onChanged(); } boolean getInstalled(int userId) { @@ -326,6 +331,7 @@ public abstract class PackageSettingBase extends SettingBase { void setInstallReason(int installReason, int userId) { modifyUserState(userId).installReason = installReason; + onChanged(); } int getUninstallReason(int userId) { @@ -334,10 +340,13 @@ public abstract class PackageSettingBase extends SettingBase { void setUninstallReason(@UninstallReason int uninstallReason, int userId) { modifyUserState(userId).uninstallReason = uninstallReason; + onChanged(); } boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) { - return modifyUserState(userId).setOverlayPaths(overlayPaths); + boolean returnValue = modifyUserState(userId).setOverlayPaths(overlayPaths); + onChanged(); + return returnValue; } OverlayPaths getOverlayPaths(int userId) { @@ -346,7 +355,10 @@ public abstract class PackageSettingBase extends SettingBase { boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths, int userId) { - return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths); + boolean returnValue = modifyUserState(userId) + .setSharedLibraryOverlayPaths(libName, overlayPaths); + onChanged(); + return returnValue; } Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) { @@ -395,6 +407,7 @@ public abstract class PackageSettingBase extends SettingBase { void setCeDataInode(long ceDataInode, int userId) { modifyUserState(userId).ceDataInode = ceDataInode; + onChanged(); } boolean getStopped(int userId) { @@ -403,6 +416,7 @@ public abstract class PackageSettingBase extends SettingBase { void setStopped(boolean stop, int userId) { modifyUserState(userId).stopped = stop; + onChanged(); } boolean getNotLaunched(int userId) { @@ -411,6 +425,7 @@ public abstract class PackageSettingBase extends SettingBase { void setNotLaunched(boolean stop, int userId) { modifyUserState(userId).notLaunched = stop; + onChanged(); } boolean getHidden(int userId) { @@ -419,6 +434,7 @@ public abstract class PackageSettingBase extends SettingBase { void setHidden(boolean hidden, int userId) { modifyUserState(userId).hidden = hidden; + onChanged(); } int getDistractionFlags(int userId) { @@ -427,6 +443,7 @@ public abstract class PackageSettingBase extends SettingBase { void setDistractionFlags(int distractionFlags, int userId) { modifyUserState(userId).distractionFlags = distractionFlags; + onChanged(); } boolean getSuspended(int userId) { @@ -487,6 +504,7 @@ public abstract class PackageSettingBase extends SettingBase { void setInstantApp(boolean instantApp, int userId) { modifyUserState(userId).instantApp = instantApp; + onChanged(); } boolean getVirtulalPreload(int userId) { @@ -495,6 +513,7 @@ public abstract class PackageSettingBase extends SettingBase { void setVirtualPreload(boolean virtualPreload, int userId) { modifyUserState(userId).virtualPreload = virtualPreload; + onChanged(); } void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, @@ -547,20 +566,24 @@ public abstract class PackageSettingBase extends SettingBase { void setEnabledComponents(ArraySet<String> components, int userId) { modifyUserState(userId).enabledComponents = components; + onChanged(); } void setDisabledComponents(ArraySet<String> components, int userId) { modifyUserState(userId).disabledComponents = components; + onChanged(); } void setEnabledComponentsCopy(ArraySet<String> components, int userId) { modifyUserState(userId).enabledComponents = components != null ? new ArraySet<String>(components) : null; + onChanged(); } void setDisabledComponentsCopy(ArraySet<String> components, int userId) { modifyUserState(userId).disabledComponents = components != null ? new ArraySet<String>(components) : null; + onChanged(); } PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) { @@ -582,10 +605,12 @@ public abstract class PackageSettingBase extends SettingBase { void addDisabledComponent(String componentClassName, int userId) { modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName); + onChanged(); } void addEnabledComponent(String componentClassName, int userId) { modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName); + onChanged(); } boolean enableComponentLPw(String componentClassName, int userId) { @@ -593,6 +618,9 @@ public abstract class PackageSettingBase extends SettingBase { boolean changed = state.disabledComponents != null ? state.disabledComponents.remove(componentClassName) : false; changed |= state.enabledComponents.add(componentClassName); + if (changed) { + onChanged(); + } return changed; } @@ -601,6 +629,9 @@ public abstract class PackageSettingBase extends SettingBase { boolean changed = state.enabledComponents != null ? state.enabledComponents.remove(componentClassName) : false; changed |= state.disabledComponents.add(componentClassName); + if (changed) { + onChanged(); + } return changed; } @@ -610,6 +641,9 @@ public abstract class PackageSettingBase extends SettingBase { ? state.disabledComponents.remove(componentClassName) : false; changed |= state.enabledComponents != null ? state.enabledComponents.remove(componentClassName) : false; + if (changed) { + onChanged(); + } return changed; } @@ -701,6 +735,7 @@ public abstract class PackageSettingBase extends SettingBase { PackageSettingBase setPath(@NonNull File path) { this.mPath = path; this.mPathString = path.toString(); + onChanged(); return this; } @@ -722,7 +757,9 @@ public abstract class PackageSettingBase extends SettingBase { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component, @Nullable String label, @Nullable Integer icon, @UserIdInt int userId) { - return modifyUserState(userId).overrideLabelAndIcon(component, label, icon); + boolean returnValue = modifyUserState(userId).overrideLabelAndIcon(component, label, icon); + onChanged(); + return returnValue; } /** @@ -732,6 +769,7 @@ public abstract class PackageSettingBase extends SettingBase { */ public void resetOverrideComponentLabelIcon(@UserIdInt int userId) { modifyUserState(userId).resetOverrideComponentLabelIcon(); + onChanged(); } /** @@ -741,6 +779,7 @@ public abstract class PackageSettingBase extends SettingBase { */ public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) { modifyUserState(userId).splashScreenTheme = themeName; + onChanged(); } /** @@ -776,6 +815,7 @@ public abstract class PackageSettingBase extends SettingBase { */ public void setStatesOnCommit() { incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString())); + onChanged(); } /** @@ -783,6 +823,7 @@ public abstract class PackageSettingBase extends SettingBase { */ public void setIncrementalStatesCallback(IncrementalStates.Callback callback) { incrementalStates.setCallback(callback); + onChanged(); } /** @@ -791,6 +832,7 @@ public abstract class PackageSettingBase extends SettingBase { */ public void setLoadingProgress(float progress) { incrementalStates.setProgress(progress); + onChanged(); } public long getFirstInstallTime() { diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index e85fa234a4ce..0f4f58b32c1b 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -228,17 +228,20 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { Vibration.Status status = null; while (!mStepQueue.isEmpty()) { - long waitTime = mStepQueue.calculateWaitTime(); - if (waitTime <= 0) { - mStepQueue.consumeNext(); - } else { - synchronized (mLock) { + long waitTime; + synchronized (mLock) { + waitTime = mStepQueue.calculateWaitTime(); + if (waitTime > 0) { try { mLock.wait(waitTime); } catch (InterruptedException e) { } } } + // If we waited, the queue may have changed, so let the loop run again. + if (waitTime <= 0) { + mStepQueue.consumeNext(); + } Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED : mStepQueue.calculateVibrationStatus(sequentialEffectSize); if (status == null && currentStatus != Vibration.Status.RUNNING) { @@ -387,15 +390,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } /** Returns the time in millis to wait before calling {@link #consumeNext()}. */ + @GuardedBy("mLock") public long calculateWaitTime() { - Step nextStep; - synchronized (mLock) { - if (!mPendingOnVibratorCompleteSteps.isEmpty()) { - // Steps anticipated by vibrator complete callback should be played right away. - return 0; - } - nextStep = mNextSteps.peek(); + if (!mPendingOnVibratorCompleteSteps.isEmpty()) { + // Steps anticipated by vibrator complete callback should be played right away. + return 0; } + Step nextStep = mNextSteps.peek(); return nextStep == null ? 0 : nextStep.calculateWaitTime(); } @@ -603,7 +604,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return false; } - /** Returns the time in millis to wait before playing this step. */ + /** + * Returns the time in millis to wait before playing this step. This is performed + * while holding the queue lock, so should not rely on potentially slow operations. + */ public long calculateWaitTime() { if (startTime == Long.MAX_VALUE) { // This step don't have a predefined start time, it's just marked to be executed diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4c1142257218..1b799dfbdaab 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -733,15 +733,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // dismissing during the task switching to keep the window focus because IME window has // higher window hierarchy, we don't give it focus if the next IME layering target // doesn't request IME visible. - if (w.mIsImWindow && (mImeLayeringTarget == null + if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null || !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME))) { - if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { - return false; - } - - if (w.isChildWindow()) { - return false; - } + return false; + } + if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null + && !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME) + && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION, + ANIMATION_TYPE_APP_TRANSITION)) { + return false; } final ActivityRecord activity = w.mActivityRecord; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 11a0fba4b015..f2f192686ad5 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -32,10 +32,6 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_B 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; @@ -215,7 +211,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); - return adjustVisibilityForIme(target, state, state == originalState); + return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; } /** @@ -245,38 +241,16 @@ class InsetsPolicy { return state; } - private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, + // Navigation bar insets is always visible to IME. + private static InsetsState adjustVisibilityForIme(InsetsState originalState, boolean copyState) { - 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; - } + 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; } return originalState; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 672ebf10dcf0..fe1020c86041 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -374,7 +374,9 @@ class KeyguardController { // can be committed before KEYGUARD_OCCLUDE transition is handled. // Set mRequestForceTransition flag to make sure that the app transition animation // is applied for such case. - if (topActivity != null) { + // TODO(b/194243906): Fix this before enabling the remote keyguard animation. + if (WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation + && topActivity != null) { topActivity.mRequestForceTransition = true; } updateKeyguardSleepToken(DEFAULT_DISPLAY); diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java index c12eb32a9143..e98a4dded99b 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java @@ -64,12 +64,16 @@ public class AppOpsStartedWatcherTest { .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), - eq(AppOpsManager.MODE_ALLOWED)); + eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), + eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), + eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), - eq(AppOpsManager.MODE_ALLOWED)); + eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), + eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), + eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); // Stop watching appOpsManager.stopWatchingStarted(listener); @@ -94,7 +98,9 @@ public class AppOpsStartedWatcherTest { .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), - eq(AppOpsManager.MODE_ALLOWED)); + eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), + eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), + eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); // Finish up diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 8592166aae15..f4d14995f7c7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -378,6 +378,11 @@ public class BiometricSchedulerTest { protected void handleLifecycleAfterAuth(boolean authenticated) { } + + @Override + public boolean wasUserDetected() { + return false; + } } private static class TestAuthenticationClient extends AuthenticationClient<Object> { @@ -407,6 +412,11 @@ public class BiometricSchedulerTest { protected void handleLifecycleAfterAuth(boolean authenticated) { } + + @Override + public boolean wasUserDetected() { + return false; + } } private static class TestClientMonitor2 extends TestClientMonitor { 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 7f5f3c2df077..bfb0be760f85 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 @@ -32,6 +32,7 @@ import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import android.content.Context; +import android.hardware.biometrics.BiometricConstants; import android.os.Handler; import android.os.Looper; import android.platform.test.annotations.Presubmit; @@ -61,6 +62,8 @@ public class CoexCoordinatorTest { private Context mContext; @Mock private CoexCoordinator.Callback mCallback; + @Mock + private CoexCoordinator.ErrorCallback mErrorCallback; @Before public void setUp() { @@ -255,13 +258,16 @@ public class CoexCoordinatorTest { mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient); mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient); + // For easier reading + final CoexCoordinator.Callback faceCallback = mCallback; + mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient, - mCallback); - verify(mCallback, never()).sendHapticFeedback(); - verify(mCallback, never()).sendAuthenticationResult(anyBoolean()); + faceCallback); + verify(faceCallback, never()).sendHapticFeedback(); + verify(faceCallback, never()).sendAuthenticationResult(anyBoolean()); // CoexCoordinator requests the system to hold onto this AuthenticationClient until // UDFPS result is known - verify(mCallback, never()).handleLifecycleAfterAuth(); + verify(faceCallback, never()).handleLifecycleAfterAuth(); // Reset the mock CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class); @@ -274,6 +280,8 @@ public class CoexCoordinatorTest { verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */); verify(udfpsCallback).handleLifecycleAfterAuth(); + verify(faceCallback).sendAuthenticationCanceled(); + assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty()); } else { mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient, @@ -281,16 +289,16 @@ public class CoexCoordinatorTest { if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) { verify(udfpsCallback, never()).sendHapticFeedback(); - verify(mCallback).sendHapticFeedback(); - verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */); - verify(mCallback).handleLifecycleAfterAuth(); + verify(faceCallback).sendHapticFeedback(); + verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */); + verify(faceCallback).handleLifecycleAfterAuth(); assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty()); } else { assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty()); - verify(mCallback, never()).sendHapticFeedback(); - verify(mCallback, never()).sendAuthenticationResult(anyBoolean()); + verify(faceCallback, never()).sendHapticFeedback(); + verify(faceCallback, never()).sendAuthenticationResult(anyBoolean()); verify(udfpsCallback).sendHapticFeedback(); verify(udfpsCallback) @@ -485,4 +493,82 @@ public class CoexCoordinatorTest { verify(callback).handleLifecycleAfterAuth(); verify(successfulAuths).remove(eq(auth)); } + + @Test + public void testBiometricPrompt_FaceError() { + mCoexCoordinator.reset(); + + AuthenticationClient<?> client = mock(AuthenticationClient.class); + when(client.isBiometricPrompt()).thenReturn(true); + when(client.wasAuthAttempted()).thenReturn(true); + + mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client); + + mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, + mErrorCallback); + verify(mErrorCallback).sendHapticFeedback(); + } + + @Test + public void testKeyguard_faceAuthOnly_errorWhenBypassEnabled() { + testKeyguard_faceAuthOnly(true /* bypassEnabled */); + } + + @Test + public void testKeyguard_faceAuthOnly_errorWhenBypassDisabled() { + testKeyguard_faceAuthOnly(false /* bypassEnabled */); + } + + private void testKeyguard_faceAuthOnly(boolean bypassEnabled) { + mCoexCoordinator.reset(); + + AuthenticationClient<?> client = mock(AuthenticationClient.class); + when(client.isKeyguard()).thenReturn(true); + when(client.isKeyguardBypassEnabled()).thenReturn(bypassEnabled); + when(client.wasAuthAttempted()).thenReturn(true); + when(client.wasUserDetected()).thenReturn(true); + + mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client); + + mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, + mErrorCallback); + verify(mErrorCallback).sendHapticFeedback(); + } + + @Test + public void testKeyguard_coex_faceErrorWhenBypassEnabled() { + testKeyguard_coex_faceError(true /* bypassEnabled */); + } + + @Test + public void testKeyguard_coex_faceErrorWhenBypassDisabled() { + testKeyguard_coex_faceError(false /* bypassEnabled */); + } + + private void testKeyguard_coex_faceError(boolean bypassEnabled) { + mCoexCoordinator.reset(); + + AuthenticationClient<?> faceClient = mock(AuthenticationClient.class); + when(faceClient.isKeyguard()).thenReturn(true); + when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled); + when(faceClient.wasAuthAttempted()).thenReturn(true); + when(faceClient.wasUserDetected()).thenReturn(true); + + 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); + + mCoexCoordinator.onAuthenticationError(faceClient, + BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback); + + if (bypassEnabled) { + verify(mErrorCallback).sendHapticFeedback(); + } else { + verify(mErrorCallback, never()).sendHapticFeedback(); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index a205a1d167fe..1ac28abb4c2f 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -1340,11 +1340,19 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); director.start(createMockSensorManager()); - ArgumentCaptor<ProximityActiveListener> captor = + ArgumentCaptor<ProximityActiveListener> ProximityCaptor = ArgumentCaptor.forClass(ProximityActiveListener.class); verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class), - captor.capture()); - ProximityActiveListener listener = captor.getValue(); + ProximityCaptor.capture()); + ProximityActiveListener proximityListener = ProximityCaptor.getValue(); + + ArgumentCaptor<DisplayListener> DisplayCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(DisplayCaptor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED + | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener displayListener = DisplayCaptor.getValue(); // Verify that there is no proximity vote initially Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); @@ -1353,13 +1361,38 @@ public class DisplayModeDirectorTest { when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null), eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60)); + when(mInjector.isDozeState(any(Display.class))).thenReturn(false); + // Set the proximity to active and verify that we added a vote. - listener.onProximityActive(true); + proximityListener.onProximityActive(true); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertVoteForRefreshRate(vote, 60.f); + + // Set the display state to doze and verify that the vote is gone + when(mInjector.isDozeState(any(Display.class))).thenReturn(true); + displayListener.onDisplayAdded(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + + // Set the display state to on and verify that we added the vote back. + when(mInjector.isDozeState(any(Display.class))).thenReturn(false); + displayListener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertVoteForRefreshRate(vote, 60.f); + + // Set the display state to doze and verify that the vote is gone + when(mInjector.isDozeState(any(Display.class))).thenReturn(true); + displayListener.onDisplayAdded(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + + // Remove the display to cause the doze state to be removed + displayListener.onDisplayRemoved(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); assertVoteForRefreshRate(vote, 60.f); // Turn prox off and verify vote is gone. - listener.onProximityActive(false); + proximityListener.onProximityActive(false); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); assertNull(vote); } @@ -1710,6 +1743,11 @@ public class DisplayModeDirectorTest { return null; } + @Override + public boolean isDozeState(Display d) { + return false; + } + void notifyPeakRefreshRateChanged() { if (mPeakRefreshRateObserver != null) { mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 61b5c2b09bb2..4cc4d55f228d 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -83,7 +83,7 @@ import java.util.stream.Collectors; @Presubmit public class VibrationThreadTest { - private static final int TEST_TIMEOUT_MILLIS = 1_000; + private static final int TEST_TIMEOUT_MILLIS = 900; private static final int UID = Process.ROOT_UID; private static final int VIBRATOR_ID = 1; private static final String PACKAGE_NAME = "package"; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index c60b8dcf67cf..12fc2f4ea1f4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -2223,6 +2223,9 @@ public class DisplayContentTests extends WindowTestsBase { // request IME visible. final WindowState nextImeAppTarget = createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget"); + spyOn(nextImeAppTarget); + doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION, + ANIMATION_TYPE_APP_TRANSITION); mDisplayContent.setImeLayeringTarget(nextImeAppTarget); assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow()); } 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 3a50bb0f06bc..92b670ed9699 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -53,9 +53,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 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; @@ -894,36 +891,6 @@ public class WindowStateTests extends WindowTestsBase { 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/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 965f126000fd..734172fc1549 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -36,6 +36,7 @@ import android.content.PermissionChecker; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; import android.media.AudioFormat; +import android.media.AudioManagerInternal; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.os.Binder; @@ -44,6 +45,7 @@ import android.os.IBinder; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SharedMemory; @@ -275,6 +277,7 @@ final class HotwordDetectionConnection { LocalServices.getService(PermissionManagerServiceInternal.class) .setHotwordDetectionServiceProvider(null); mIdentity = null; + updateServiceUidForAudioPolicy(Process.INVALID_UID); } mCancellationTaskFuture.cancel(/* may interrupt */ true); if (mAudioFlinger != null) { @@ -893,6 +896,8 @@ final class HotwordDetectionConnection { connection.run(service -> service.ping(new IRemoteCallback.Stub() { @Override public void sendResult(Bundle bundle) throws RemoteException { + // TODO: Exit if the service has been unbound already (though there's a very low + // chance this happens). if (DEBUG) { Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid()); } @@ -902,10 +907,21 @@ final class HotwordDetectionConnection { LocalServices.getService(PermissionManagerServiceInternal.class) .setHotwordDetectionServiceProvider(() -> uid); mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid); + updateServiceUidForAudioPolicy(uid); } })); } + private void updateServiceUidForAudioPolicy(int uid) { + mScheduledExecutorService.execute(() -> { + final AudioManagerInternal audioManager = + LocalServices.getService(AudioManagerInternal.class); + if (audioManager != null) { + audioManager.setHotwordDetectionServiceUid(uid); + } + }); + } + private static void bestEffortClose(Closeable closeable) { try { closeable.close(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java index 68b2e6168b5c..c0c3e6f530db 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java @@ -60,7 +60,7 @@ final class SoundTriggerSessionPermissionsDecorator implements @Override public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { - // No permission needed. + // No permission needed here (the app must have the Assistant Role to retrieve the session). return mDelegate.getDspModuleProperties(); } @@ -71,7 +71,9 @@ final class SoundTriggerSessionPermissionsDecorator implements if (DEBUG) { Slog.d(TAG, "startRecognition"); } - enforcePermissions(); + if (!isHoldingPermissions()) { + return SoundTrigger.STATUS_PERMISSION_DENIED; + } return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, recognitionConfig, b); } @@ -80,25 +82,28 @@ final class SoundTriggerSessionPermissionsDecorator implements public int stopRecognition(int i, IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) throws RemoteException { - enforcePermissions(); + // Stopping a model does not require special permissions. Having a handle to the session is + // sufficient. return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); } @Override public int setParameter(int i, int i1, int i2) throws RemoteException { - enforcePermissions(); + if (!isHoldingPermissions()) { + return SoundTrigger.STATUS_PERMISSION_DENIED; + } return mDelegate.setParameter(i, i1, i2); } @Override public int getParameter(int i, int i1) throws RemoteException { - enforcePermissions(); + // No permission needed here (the app must have the Assistant Role to retrieve the session). return mDelegate.getParameter(i, i1); } @Override public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { - enforcePermissions(); + // No permission needed here (the app must have the Assistant Role to retrieve the session). return mDelegate.queryParameter(i, i1); } @@ -109,9 +114,15 @@ final class SoundTriggerSessionPermissionsDecorator implements } // TODO: Share this code with SoundTriggerMiddlewarePermission. - private void enforcePermissions() { - enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO); - enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD); + private boolean isHoldingPermissions() { + try { + enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO); + enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD); + return true; + } catch (SecurityException e) { + Slog.e(TAG, e.toString()); + return false; + } } /** diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 71541ad729d5..9ea2b7b12ad0 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1896,17 +1896,19 @@ public class VoiceInteractionManagerService extends SystemService { String serviceComponentName = serviceInfo.getComponentName() .flattenToShortString(); - - String serviceRecognizerName = new ComponentName(pkg, - voiceInteractionServiceInfo.getRecognitionService()) - .flattenToShortString(); + if (voiceInteractionServiceInfo.getRecognitionService() == null) { + Slog.e(TAG, "The RecognitionService must be set to avoid boot " + + "loop on earlier platform version. Also make sure that this " + + "is a valid RecognitionService when running on Android 11 " + + "or earlier."); + serviceComponentName = ""; + } Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.ASSISTANT, serviceComponentName, userId); Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName, userId); - return; } @@ -1947,6 +1949,29 @@ public class VoiceInteractionManagerService extends SystemService { } } + private void resetServicesIfNoRecognitionService(ComponentName serviceComponent, + int userHandle) { + for (ResolveInfo resolveInfo : queryInteractorServices(userHandle, + serviceComponent.getPackageName())) { + VoiceInteractionServiceInfo serviceInfo = + new VoiceInteractionServiceInfo( + mContext.getPackageManager(), + resolveInfo.serviceInfo); + if (!serviceInfo.getSupportsAssist()) { + continue; + } + if (serviceInfo.getRecognitionService() == null) { + Slog.e(TAG, "The RecognitionService must be set to " + + "avoid boot loop on earlier platform version. " + + "Also make sure that this is a valid " + + "RecognitionService when running on Android 11 " + + "or earlier."); + setCurInteractor(null, userHandle); + resetCurAssistant(userHandle); + } + } + } + PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { @@ -2090,6 +2115,7 @@ public class VoiceInteractionManagerService extends SystemService { change = isPackageAppearing(curInteractor.getPackageName()); if (change != PACKAGE_UNCHANGED) { + resetServicesIfNoRecognitionService(curInteractor, userHandle); // If current interactor is now appearing, for any reason, then // restart our connection with it. if (mImpl != null && curInteractor.getPackageName().equals( @@ -2112,6 +2138,13 @@ public class VoiceInteractionManagerService extends SystemService { initForUser(userHandle); return; } + change = isPackageAppearing(curAssistant.getPackageName()); + if (change != PACKAGE_UNCHANGED) { + // It is possible to update Assistant without a voice interactor to one + // with a voice-interactor. We should make sure the recognition service + // is set to avoid boot loop. + resetServicesIfNoRecognitionService(curAssistant, userHandle); + } } // There is no interactor, so just deal with a simple recognizer. diff --git a/telephony/OWNERS b/telephony/OWNERS index 628c48070314..4df8a4bc6413 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -4,13 +4,14 @@ amitmahajan@google.com breadley@google.com fionaxu@google.com jackyu@google.com -hallliu@google.com rgreenwalt@google.com tgunn@google.com jminjie@google.com shuoq@google.com -refuhoo@google.com nazaninb@google.com sarahchin@google.com -dbright@google.com xiaotonj@google.com +huiwang@google.com +jayachandranc@google.com +chinmayd@google.com +amruthr@google.com diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java index c18ab33eb2c9..f0048248a5cc 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -193,6 +193,10 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe return mDelegateBinder; } + public ISipDelegateStateCallback getStateCallbackBinder() { + return mStateBinder; + } + private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) { String transactionId = m.getViaBranchParameter(); SipDelegate d = mDelegate; diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index 1f74c09af0f6..13ea99735ab4 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -21,6 +21,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Binder; import android.os.IBinder; +import android.os.RemoteException; import android.telephony.ims.DelegateMessageCallback; import android.telephony.ims.DelegateRequest; import android.telephony.ims.DelegateStateCallback; @@ -33,6 +34,7 @@ import android.telephony.ims.aidl.SipDelegateAidlWrapper; import android.util.Log; import java.util.ArrayList; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.Executor; @@ -49,10 +51,15 @@ import java.util.concurrent.Executor; public class SipTransportImplBase { private static final String LOG_TAG = "SipTransportIB"; - private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { - mBinderExecutor.execute(() -> binderDiedInternal()); + // Clean up all binders in this case. + mBinderExecutor.execute(() -> binderDiedInternal(null)); + } + @Override + public void binderDied(IBinder who) { + mBinderExecutor.execute(() -> binderDiedInternal(who)); } }; @@ -142,6 +149,7 @@ public class SipTransportImplBase { ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) { SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc); mDelegates.add(wrapper); + linkDeathRecipient(wrapper); createSipDelegate(subId, r, wrapper, wrapper); } @@ -155,6 +163,7 @@ public class SipTransportImplBase { } if (result != null) { + unlinkDeathRecipient(result); mDelegates.remove(result); destroySipDelegate(result.getDelegate(), reason); } else { @@ -163,12 +172,37 @@ public class SipTransportImplBase { } } - private void binderDiedInternal() { + private void linkDeathRecipient(SipDelegateAidlWrapper w) { + try { + w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up."); + mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder()); + } + } + + private void unlinkDeathRecipient(SipDelegateAidlWrapper w) { + try { + w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + // Ignore this case. + } + } + + private void binderDiedInternal(IBinder who) { for (SipDelegateAidlWrapper w : mDelegates) { - destroySipDelegate(w.getDelegate(), - SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); + // If the binder itself was not given from the platform, just clean up all binders. + if (who == null || w.getStateCallbackBinder().asBinder().equals(who)) { + Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and " + + "removing."); + mDelegates.remove(w); + destroySipDelegate(w.getDelegate(), + SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); + return; + } } - mDelegates.clear(); + Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching " + + "SipDelegate"); } /** |