diff options
116 files changed, 2505 insertions, 781 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d6a067d35f9b..2c0b6e9ee89d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -800,6 +800,7 @@ package android.content.pm { method public boolean hasRequestForegroundServiceExemption(); method public boolean isPrivilegedApp(); method public boolean isSystemApp(); + method public void setEnableOnBackInvokedCallback(boolean); field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8 field public int privateFlags; } diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index 436eac37b4fb..7c83d5850f76 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -45,7 +45,9 @@ import java.util.Set; * referenced in the manifest via {@code android:localeConfig} on * {@code <application>}. * - * For more information, see TODO(b/214154050): add link to guide + * <p>For more information, see + * <a href="https://developer.android.com/about/versions/13/features/app-languages#use-localeconfig"> + * the section on per-app language preferences</a>. * * @attr ref android.R.styleable#LocaleConfig_Locale_name * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2961b5505794..24c383692c09 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -2752,4 +2752,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US)); } } + + /** + * Sets whether the application will use the {@link android.window.OnBackInvokedCallback} + * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related + * callbacks. Intended to be used from tests only. + * + * @see #isOnBackInvokedCallbackEnabled() + * @hide + */ + @TestApi + public void setEnableOnBackInvokedCallback(boolean isEnable) { + if (isEnable) { + privateFlagsExt |= PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK; + } else { + privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK; + } + } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 44dc28d2b0fa..52e64e80e6d4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2618,6 +2618,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2689,6 +2698,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 6d74b819301d..bde71bb90bf7 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -315,6 +315,15 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Parsed package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -367,16 +376,29 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } - if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); - } - // If it's a pre-release SDK and the codename matches this platform, it // definitely targets this SDK. if (matchTargetCode(platformSdkCodenames, targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Parsed package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + + try { + if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + } catch (IllegalArgumentException e) { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index f9ed0e3db499..3f49b73a78fc 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -37,6 +37,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSubtype; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.inputmethod.CancellationGroup; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -194,7 +195,9 @@ class IInputMethodWrapper extends IInputMethod.Stub case DO_START_INPUT: { final SomeArgs args = (SomeArgs) msg.obj; final IBinder startInputToken = (IBinder) args.arg1; - final IInputContext inputContext = (IInputContext) args.arg2; + final IInputContext inputContext = (IInputContext) ((SomeArgs) args.arg2).arg1; + final ImeOnBackInvokedDispatcher imeDispatcher = + (ImeOnBackInvokedDispatcher) ((SomeArgs) args.arg2).arg2; final EditorInfo info = (EditorInfo) args.arg3; final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4; final boolean restarting = args.argi5 == 1; @@ -205,7 +208,7 @@ class IInputMethodWrapper extends IInputMethod.Stub : null; info.makeCompatible(mTargetSdkVersion); inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken, - navButtonFlags); + navButtonFlags, imeDispatcher); args.recycle(); return; } @@ -348,13 +351,17 @@ class IInputMethodWrapper extends IInputMethod.Stub @Override public void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute, boolean restarting, - @InputMethodNavButtonFlags int navButtonFlags) { + @InputMethodNavButtonFlags int navButtonFlags, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { if (mCancellationGroup == null) { Log.e(TAG, "startInput must be called after bindInput."); mCancellationGroup = new CancellationGroup(); } + SomeArgs args = SomeArgs.obtain(); + args.arg1 = inputContext; + args.arg2 = imeDispatcher; mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken, - inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags)); + args, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index efd4f0681838..25296bc0a8b9 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -101,6 +101,7 @@ import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -134,6 +135,9 @@ import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import android.window.ImeOnBackInvokedDispatcher; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import android.window.WindowMetricsHelper; import com.android.internal.annotations.GuardedBy; @@ -344,6 +348,9 @@ public class InputMethodService extends AbstractInputMethodService { * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view. **/ private RingBuffer<MotionEvent> mPendingEvents; + private ImeOnBackInvokedDispatcher mImeDispatcher; + private Boolean mBackCallbackRegistered = false; + private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack; /** * Returns whether {@link InputMethodService} is responsible for rendering the back button and @@ -797,7 +804,13 @@ public class InputMethodService extends AbstractInputMethodService { @Override public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) { + @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { + mImeDispatcher = imeDispatcher; + if (mWindow != null) { + mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher( + imeDispatcher); + } mPrivOps.reportStartInputAsync(startInputToken); mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags); if (restarting) { @@ -1496,6 +1509,10 @@ public class InputMethodService extends AbstractInputMethodService { Context.LAYOUT_INFLATER_SERVICE); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow"); mWindow = new SoftInputWindow(this, mTheme, mDispatcherState); + if (mImeDispatcher != null) { + mWindow.getOnBackInvokedDispatcher() + .setImeOnBackInvokedDispatcher(mImeDispatcher); + } mNavigationBarController.onSoftInputWindowCreated(mWindow); { final Window window = mWindow.getWindow(); @@ -1608,6 +1625,8 @@ public class InputMethodService extends AbstractInputMethodService { // when IME developers are doing something unsupported. InputMethodPrivilegedOperationsRegistry.remove(mToken); } + unregisterCompatOnBackInvokedCallback(); + mImeDispatcher = null; } /** @@ -2568,8 +2587,46 @@ public class InputMethodService extends AbstractInputMethodService { cancelImeSurfaceRemoval(); mInShowWindow = false; Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + registerCompatOnBackInvokedCallback(); + } + + + /** + * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time + * back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code + * around to handle back on older devices. + */ + private void registerCompatOnBackInvokedCallback() { + if (mBackCallbackRegistered) { + return; + } + if (mWindow != null) { + mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback); + mBackCallbackRegistered = true; + } + } + + private void unregisterCompatOnBackInvokedCallback() { + if (!mBackCallbackRegistered) { + return; + } + if (mWindow != null) { + mWindow.getOnBackInvokedDispatcher() + .unregisterOnBackInvokedCallback(mCompatBackCallback); + mBackCallbackRegistered = false; + } } + private KeyEvent createBackKeyEvent(int action, boolean isTracking) { + final long when = SystemClock.uptimeMillis(); + return new KeyEvent(when, when, action, + KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, + KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY + | (isTracking ? KeyEvent.FLAG_TRACKING : 0), + InputDevice.SOURCE_KEYBOARD); + } private boolean prepareWindow(boolean showInput) { boolean doShowInput = false; @@ -2658,6 +2715,7 @@ public class InputMethodService extends AbstractInputMethodService { } mLastWasInFullscreenMode = mIsFullscreen; updateFullscreenMode(); + unregisterCompatOnBackInvokedCallback(); } /** @@ -3797,4 +3855,14 @@ public class InputMethodService extends AbstractInputMethodService { proto.end(token); } }; + + private void compatHandleBack() { + final KeyEvent downEvent = createBackKeyEvent( + KeyEvent.ACTION_DOWN, false /* isTracking */); + onKeyDown(KeyEvent.KEYCODE_BACK, downEvent); + final boolean hasStartedTracking = + (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0; + final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking); + onKeyUp(KeyEvent.KEYCODE_BACK, upEvent); + } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 37f44e98c165..9a2f7baa7265 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -738,6 +738,14 @@ public final class DeviceConfig { */ public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native"; + /** + * Namespace for DevicePolicyManager related features. + * + * @hide + */ + public static final String NAMESPACE_DEVICE_POLICY_MANAGER = + "device_policy_manager"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dac54cf6146e..f35a45891545 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9695,6 +9695,40 @@ public final class Settings { "active_unlock_on_biometric_fail"; /** + * If active unlock triggers on biometric failures, include the following error codes + * as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}. + * Error codes should be separated by a pipe. For example: "1|4|5". If active unlock + * should never trigger on any face errors, this should be set to an empty string. + * A null value will use the system default value (TIMEOUT). + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_FACE_ERRORS = + "active_unlock_on_face_errors"; + + /** + * If active unlock triggers on biometric failures, include the following acquired info + * as a "biometric failure". See {@link android.hardware.biometrics.BiometricFaceConstants}. + * Acquired codes should be separated by a pipe. For example: "1|4|5". If active unlock + * should never on trigger on any acquired info messages, this should be + * set to an empty string. A null value will use the system default value (none). + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO = + "active_unlock_on_face_acquire_info"; + + /** + * If active unlock triggers on biometric failures, then also request active unlock on + * unlock intent when each setting (BiometricType) is the only biometric type enrolled. + * Biometric types should be separated by a pipe. For example: "0|3" or "0". If this + * setting should be disabled, then this should be set to an empty string. A null value + * will use the system default value (0 / None). + * 0 = None, 1 = Any face, 2 = Any fingerprint, 3 = Under display fingerprint + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED = + "active_unlock_on_unlock_intent_when_biometric_enrolled"; + + /** * Whether the assist gesture should be enabled. * * @hide diff --git a/core/java/android/service/quickaccesswallet/TEST_MAPPING b/core/java/android/service/quickaccesswallet/TEST_MAPPING index 4d97ab6e612d..5d2a3a806a7b 100644 --- a/core/java/android/service/quickaccesswallet/TEST_MAPPING +++ b/core/java/android/service/quickaccesswallet/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-large": [ { "name": "CtsQuickAccessWalletTestCases" } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2475e2cf3dd8..d04b07c13b41 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9922,7 +9922,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <ol> * <li>It should only be called when content capture is enabled for the view. * <li>It must call viewAppeared() before viewDisappeared() - * <li>viewAppearead() can only be called when the view is visible and laidout + * <li>viewAppeared() can only be called when the view is visible and laid out * <li>It should not call the same event twice. * </ol> */ @@ -9999,6 +9999,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this); } } + + // We reset any translation state as views may be re-used (e.g., as in ListView and + // RecyclerView). We only need to do this for views important for content capture since + // views unimportant for content capture won't be translated anyway. + clearTranslationState(); } } @@ -12719,6 +12724,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public void clearTranslationState() { + if (mViewTranslationCallback != null) { + mViewTranslationCallback.onClearTranslation(this); + } + clearViewTranslationCallback(); + clearViewTranslationResponse(); + if (hasTranslationTransientState()) { + setHasTransientState(false); + setHasTranslationTransientState(false); + } + } + + /** * Returns true if this view is currently attached to a window. */ public boolean isAttachedToWindow() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0bdbfbcf0dfc..48c102bd4883 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6095,6 +6095,28 @@ public final class ViewRootImpl implements ViewParent, @Override protected int onProcess(QueuedInputEvent q) { + if (q.mEvent instanceof KeyEvent) { + final KeyEvent event = (KeyEvent) q.mEvent; + + // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the + // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}. + if (isBack(event) + && mContext != null + && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { + OnBackInvokedCallback topCallback = + getOnBackInvokedDispatcher().getTopCallback(); + if (event.getAction() == KeyEvent.ACTION_UP) { + if (topCallback != null) { + topCallback.onBackInvoked(); + return FINISH_HANDLED; + } + } else { + // Drop other actions such as {@link KeyEvent.ACTION_DOWN}. + return FINISH_NOT_HANDLED; + } + } + } + if (mInputQueue != null && q.mEvent instanceof KeyEvent) { mInputQueue.sendInputEvent(q.mEvent, q, true, this); return DEFER; @@ -6446,24 +6468,6 @@ public final class ViewRootImpl implements ViewParent, return FINISH_HANDLED; } - // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the - // view tree and invoke the appropriate {@link OnBackInvokedCallback}. - if (isBack(event) - && mContext != null - && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { - OnBackInvokedCallback topCallback = - getOnBackInvokedDispatcher().getTopCallback(); - if (event.getAction() == KeyEvent.ACTION_UP) { - if (topCallback != null) { - topCallback.onBackInvoked(); - return FINISH_HANDLED; - } - } else { - // Drop other actions such as {@link KeyEvent.ACTION_DOWN}. - return FINISH_NOT_HANDLED; - } - } - // Deliver the key to the view hierarchy. if (mView.dispatchKeyEvent(event)) { return FINISH_HANDLED; diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index 6209b46997e8..dbdc0daff2c1 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.InputChannel; import android.view.MotionEvent; import android.view.View; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodNavButtonFlags; @@ -232,6 +233,10 @@ public interface InputMethod { * long as your implementation of {@link InputMethod} relies on such * IPCs * @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session. + * @param imeDispatcher The {@link ImeOnBackInvokedDispatcher }} to be set on the + * IME's {@link android.window.WindowOnBackInvokedDispatcher}, so that IME + * {@link android.window.OnBackInvokedCallback}s can be forwarded to + * the client requesting to start input. * @see #startInput(InputConnection, EditorInfo) * @see #restartInput(InputConnection, EditorInfo) * @see EditorInfo @@ -240,7 +245,8 @@ public interface InputMethod { @MainThread default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) { + @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { if (restarting) { restartInput(inputConnection, editorInfo); } else { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d9bde5825fde..e2e9a8557793 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -91,6 +91,8 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillManager; +import android.window.ImeOnBackInvokedDispatcher; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.DirectBootAwareness; @@ -105,6 +107,7 @@ import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; import com.android.internal.os.SomeArgs; +import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; @@ -279,6 +282,21 @@ public final class InputMethodManager { private static final String SUBTYPE_MODE_VOICE = "voice"; /** + * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus( + * int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive + * {@link android.window.OnBackInvokedCallback} registrations from IME. + */ + private final ImeOnBackInvokedDispatcher mImeDispatcher = + new ImeOnBackInvokedDispatcher(Handler.getMain()) { + @Override + public WindowOnBackInvokedDispatcher getReceivingDispatcher() { + synchronized (mH) { + return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null; + } + } + }; + + /** * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly * or indirectly relied on {@link #sInstance} via reflection or something like that. * @@ -740,7 +758,8 @@ public final class InputMethodManager { windowFlags, null, null, null, - mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + mCurRootView.mContext.getApplicationInfo().targetSdkVersion, + mImeDispatcher); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1687,6 +1706,8 @@ public final class InputMethodManager { mServedConnecting = false; clearConnectionLocked(); } + // Clear the back callbacks held by the ime dispatcher to avoid memory leaks. + mImeDispatcher.clear(); } public void displayCompletions(View view, CompletionInfo[] completions) { @@ -2359,7 +2380,8 @@ public final class InputMethodManager { softInputMode, windowFlags, tba, servedInputConnection, servedInputConnection == null ? null : servedInputConnection.asIRemoteAccessibilityInputConnection(), - view.getContext().getApplicationInfo().targetSdkVersion); + view.getContext().getApplicationInfo().targetSdkVersion, + mImeDispatcher); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 8cf032bc03cb..6bf2474beb17 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -158,12 +158,7 @@ public class UiTranslationController implements Dumpable { case STATE_UI_TRANSLATION_FINISHED: destroyTranslators(); runForEachView((view, callback) -> { - callback.onClearTranslation(view); - view.clearViewTranslationResponse(); - if (view.hasTranslationTransientState()) { - view.setHasTransientState(false); - view.setHasTranslationTransientState(false); - } + view.clearTranslationState(); }); notifyTranslationFinished(/* activityDestroyed= */ false); synchronized (mLock) { diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.aidl b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl new file mode 100644 index 000000000000..04e64203dd39 --- /dev/null +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/os/ParcelFileDescriptor.aidl +** +** Copyright 2022, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.window; + +parcelable ImeOnBackInvokedDispatcher; diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java new file mode 100644 index 000000000000..d5763aa25884 --- /dev/null +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.util.Log; + +import java.util.HashMap; + +/** + * A {@link OnBackInvokedDispatcher} for IME that forwards {@link OnBackInvokedCallback} + * registrations from the IME process to the app process to be registered on the app window. + * <p> + * The app process creates and propagates an instance of {@link ImeOnBackInvokedDispatcher} + * to the IME to be set on the IME window's {@link WindowOnBackInvokedDispatcher}. + * <p> + * @see WindowOnBackInvokedDispatcher#setImeOnBackInvokedDispatcher + * + * @hide + */ +public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parcelable { + + private static final String TAG = "ImeBackDispatcher"; + static final String RESULT_KEY_ID = "id"; + static final String RESULT_KEY_CALLBACK = "callback"; + static final String RESULT_KEY_PRIORITY = "priority"; + static final int RESULT_CODE_REGISTER = 0; + static final int RESULT_CODE_UNREGISTER = 1; + @NonNull + private final ResultReceiver mResultReceiver; + + public ImeOnBackInvokedDispatcher(Handler handler) { + mResultReceiver = new ResultReceiver(handler) { + @Override + public void onReceiveResult(int resultCode, Bundle resultData) { + WindowOnBackInvokedDispatcher dispatcher = getReceivingDispatcher(); + if (dispatcher != null) { + receive(resultCode, resultData, dispatcher); + } + } + }; + } + + /** + * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window + * that should receive the forwarded callback. + */ + @Nullable + protected WindowOnBackInvokedDispatcher getReceivingDispatcher() { + return null; + } + + ImeOnBackInvokedDispatcher(Parcel in) { + mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR); + } + + @Override + public void registerOnBackInvokedCallback( + @OnBackInvokedDispatcher.Priority int priority, + @NonNull OnBackInvokedCallback callback) { + final Bundle bundle = new Bundle(); + final IOnBackInvokedCallback iCallback = + new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback); + bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder()); + bundle.putInt(RESULT_KEY_PRIORITY, priority); + bundle.putInt(RESULT_KEY_ID, callback.hashCode()); + mResultReceiver.send(RESULT_CODE_REGISTER, bundle); + } + + @Override + public void unregisterOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback) { + Bundle bundle = new Bundle(); + bundle.putInt(RESULT_KEY_ID, callback.hashCode()); + mResultReceiver.send(RESULT_CODE_UNREGISTER, bundle); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mResultReceiver, flags); + } + + @NonNull + public static final Parcelable.Creator<ImeOnBackInvokedDispatcher> CREATOR = + new Parcelable.Creator<ImeOnBackInvokedDispatcher>() { + public ImeOnBackInvokedDispatcher createFromParcel(Parcel in) { + return new ImeOnBackInvokedDispatcher(in); + } + public ImeOnBackInvokedDispatcher[] newArray(int size) { + return new ImeOnBackInvokedDispatcher[size]; + } + }; + + private final HashMap<Integer, OnBackInvokedCallback> mImeCallbackMap = new HashMap<>(); + + private void receive( + int resultCode, Bundle resultData, + @NonNull OnBackInvokedDispatcher receivingDispatcher) { + final int callbackId = resultData.getInt(RESULT_KEY_ID); + if (resultCode == RESULT_CODE_REGISTER) { + int priority = resultData.getInt(RESULT_KEY_PRIORITY); + final IOnBackInvokedCallback callback = IOnBackInvokedCallback.Stub.asInterface( + resultData.getBinder(RESULT_KEY_CALLBACK)); + registerReceivedCallback( + callback, priority, callbackId, receivingDispatcher); + } else if (resultCode == RESULT_CODE_UNREGISTER) { + unregisterReceivedCallback(callbackId, receivingDispatcher); + } + } + + private void registerReceivedCallback( + @NonNull IOnBackInvokedCallback iCallback, + @OnBackInvokedDispatcher.Priority int priority, + int callbackId, + @NonNull OnBackInvokedDispatcher receivingDispatcher) { + final ImeOnBackInvokedCallback imeCallback = + new ImeOnBackInvokedCallback(iCallback); + mImeCallbackMap.put(callbackId, imeCallback); + receivingDispatcher.registerOnBackInvokedCallback(priority, imeCallback); + } + + private void unregisterReceivedCallback( + int callbackId, OnBackInvokedDispatcher receivingDispatcher) { + final OnBackInvokedCallback callback = mImeCallbackMap.get(callbackId); + if (callback == null) { + Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. " + + "callbackId: " + callbackId); + return; + } + receivingDispatcher.unregisterOnBackInvokedCallback(callback); + } + + /** Clears all registered callbacks on the instance. */ + public void clear() { + mImeCallbackMap.clear(); + } + + static class ImeOnBackInvokedCallback implements OnBackInvokedCallback { + @NonNull + private final IOnBackInvokedCallback mIOnBackInvokedCallback; + + ImeOnBackInvokedCallback(@NonNull IOnBackInvokedCallback iCallback) { + mIOnBackInvokedCallback = iCallback; + } + + @Override + public void onBackInvoked() { + try { + if (mIOnBackInvokedCallback != null) { + mIOnBackInvokedCallback.onBackInvoked(); + } + } catch (RemoteException e) { + Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); + } + } + + IOnBackInvokedCallback getIOnBackInvokedCallback() { + return mIOnBackInvokedCallback; + } + } +} diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java index 6bc2b5043e79..3539049af219 100644 --- a/core/java/android/window/OnBackInvokedDispatcher.java +++ b/core/java/android/window/OnBackInvokedDispatcher.java @@ -96,4 +96,19 @@ public interface OnBackInvokedDispatcher { * @hide */ default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { } + + + /** + * Sets an {@link ImeOnBackInvokedDispatcher} to forward {@link OnBackInvokedCallback}s + * from IME to the app process to be registered on the app window. + * + * Only call this on the IME window. Create the {@link ImeOnBackInvokedDispatcher} from + * the application process and override + * {@link ImeOnBackInvokedDispatcher#getReceivingDispatcher()} to point to the app + * window's {@link WindowOnBackInvokedDispatcher}. + * + * @hide + */ + default void setImeOnBackInvokedDispatcher( + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { } } diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java index 44093971a23e..bedf5038f669 100644 --- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -49,6 +49,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>(); private final Object mLock = new Object(); private OnBackInvokedDispatcher mActualDispatcher = null; + private ImeOnBackInvokedDispatcher mImeDispatcher; @Override public void registerOnBackInvokedCallback( @@ -108,6 +109,9 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { Log.v(TAG, String.format("Proxy transferring %d callbacks to %s", mCallbacks.size(), mActualDispatcher)); } + if (mImeDispatcher != null) { + mActualDispatcher.setImeOnBackInvokedDispatcher(mImeDispatcher); + } for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) { int priority = callbackPair.second; if (priority >= 0) { @@ -117,6 +121,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } } mCallbacks.clear(); + mImeDispatcher = null; } private void clearCallbacksOnDispatcher() { @@ -142,6 +147,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } synchronized (mLock) { mCallbacks.clear(); + mImeDispatcher = null; } } @@ -169,4 +175,14 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { transferCallbacksToDispatcher(); } } + + @Override + public void setImeOnBackInvokedDispatcher( + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { + if (mActualDispatcher != null) { + mActualDispatcher.setImeOnBackInvokedDispatcher(imeDispatcher); + } else { + mImeDispatcher = imeDispatcher; + } + } } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 781859cecb2c..edfdbcc1f4f8 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -55,6 +55,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { .getInt("persist.wm.debug.predictive_back", 1) != 0; private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0; + @Nullable + private ImeOnBackInvokedDispatcher mImeDispatcher; /** Convenience hashmap to quickly decide if a callback has been added. */ private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>(); @@ -94,6 +96,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private void registerOnBackInvokedCallbackUnchecked( @NonNull OnBackInvokedCallback callback, @Priority int priority) { + if (mImeDispatcher != null) { + mImeDispatcher.registerOnBackInvokedCallback(priority, callback); + return; + } if (!mOnBackInvokedCallbacks.containsKey(priority)) { mOnBackInvokedCallbacks.put(priority, new ArrayList<>()); } @@ -120,6 +126,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { + if (mImeDispatcher != null) { + mImeDispatcher.unregisterOnBackInvokedCallback(callback); + return; + } if (!mAllCallbacks.containsKey(callback)) { if (DEBUG) { Log.i(TAG, "Callback not found. returning..."); @@ -153,6 +163,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } mAllCallbacks.clear(); mOnBackInvokedCallbacks.clear(); + if (mImeDispatcher != null) { + mImeDispatcher = null; + } } private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) { @@ -160,14 +173,18 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return; } try { - if (callback == null) { - mWindowSession.setOnBackInvokedCallbackInfo(mWindow, null); - } else { + OnBackInvokedCallbackInfo callbackInfo = null; + if (callback != null) { int priority = mAllCallbacks.get(callback); - mWindowSession.setOnBackInvokedCallbackInfo( - mWindow, new OnBackInvokedCallbackInfo( - new OnBackInvokedCallbackWrapper(callback), priority)); + final IOnBackInvokedCallback iCallback = + callback instanceof ImeOnBackInvokedDispatcher + .ImeOnBackInvokedCallback + ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) + callback).getIOnBackInvokedCallback() + : new OnBackInvokedCallbackWrapper(callback); + callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority); } + mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo); if (DEBUG && callback == null) { Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s", Debug.getCallers(5, " "))); @@ -190,7 +207,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return null; } - private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { + static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) { @@ -270,4 +287,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK); } + + @Override + public void setImeOnBackInvokedDispatcher( + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { + mImeDispatcher = imeDispatcher; + } } diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 7db4243d3a83..0976f45c02b0 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -21,8 +21,10 @@ import static android.window.ConfigurationHelper.shouldUpdateResources; import android.annotation.AnyThread; import android.annotation.BinderThread; +import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; import android.app.IWindowToken; import android.app.ResourcesManager; import android.content.Context; @@ -33,7 +35,6 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.IWindowManager; @@ -42,6 +43,7 @@ import android.view.WindowManagerGlobal; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.function.pooled.PooledLambda; import java.lang.ref.WeakReference; @@ -76,7 +78,7 @@ public class WindowTokenClient extends IWindowToken.Stub { private boolean mAttachToWindowContainer; - private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Handler mHandler = ActivityThread.currentActivityThread().getHandler(); /** * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} @@ -188,8 +190,8 @@ public class WindowTokenClient extends IWindowToken.Stub { @BinderThread @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { - mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId, - true /* shouldReportConfigChange */)); + mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig, + newDisplayId, true /* shouldReportConfigChange */).recycleOnUse()); } // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService @@ -279,12 +281,16 @@ public class WindowTokenClient extends IWindowToken.Stub { @BinderThread @Override public void onWindowTokenRemoved() { - mHandler.post(() -> { - final Context context = mContextRef.get(); - if (context != null) { - context.destroy(); - mContextRef.clear(); - } - }); + mHandler.post(PooledLambda.obtainRunnable( + WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse()); + } + + @MainThread + private void onWindowTokenRemovedInner() { + final Context context = mContextRef.get(); + if (context != null) { + context.destroy(); + mContextRef.clear(); + } } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 3fee914f2def..781b6d5459ca 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -971,7 +971,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_COPY, "", - -1); + -1, + false); setResult(RESULT_OK); finish(); @@ -1155,7 +1156,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_NEARBY, "", - -1); + -1, + false); // Action bar is user-independent, always start as primary safelyStartActivityAsUser(ti, getPersonalProfileUserHandle()); finish(); @@ -1177,7 +1179,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_EDIT, "", - -1); + -1, + false); // Action bar is user-independent, always start as primary safelyStartActivityAsUser(ti, getPersonalProfileUserHandle()); finish(); @@ -1754,7 +1757,8 @@ public class ChooserActivity extends ResolverActivity implements target.getComponentName().getPackageName() + target.getTitle().toString(), mMaxHashSaltDays); - directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo); + SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo; + directTargetAlsoRanked = getRankedPosition(selectableTargetInfo); if (mCallerChooserTargets != null) { numCallerProvided = mCallerChooserTargets.length; @@ -1762,7 +1766,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_SERVICE, targetInfo.getResolveInfo().activityInfo.processName, - value + value, + selectableTargetInfo.isPinned() ); break; case ChooserListAdapter.TARGET_CALLER: @@ -1773,7 +1778,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_APP, targetInfo.getResolveInfo().activityInfo.processName, - value + value, + targetInfo.isPinned() ); break; case ChooserListAdapter.TARGET_STANDARD_AZ: @@ -1784,7 +1790,8 @@ public class ChooserActivity extends ResolverActivity implements getChooserActivityLogger().logShareTargetSelected( SELECTION_TYPE_STANDARD, targetInfo.getResolveInfo().activityInfo.processName, - value + value, + false ); break; } diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java index 321730786471..bb7d50af4200 100644 --- a/core/java/com/android/internal/app/ChooserActivityLogger.java +++ b/core/java/com/android/internal/app/ChooserActivityLogger.java @@ -34,7 +34,8 @@ public interface ChooserActivityLogger { int appProvidedApp, boolean isWorkprofile, int previewType, String intent); /** Logs a UiEventReported event for the system sharesheet when the user selects a target. */ - void logShareTargetSelected(int targetType, String packageName, int positionPicked); + void logShareTargetSelected(int targetType, String packageName, int positionPicked, + boolean isPinned); /** Logs a UiEventReported event for the system sharesheet being triggered by the user. */ default void logSharesheetTriggered() { diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java index 48bdba3f5dae..e3cc4f12fcc6 100644 --- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java +++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java @@ -51,12 +51,14 @@ public class ChooserActivityLoggerImpl implements ChooserActivityLogger { } @Override - public void logShareTargetSelected(int targetType, String packageName, int positionPicked) { + public void logShareTargetSelected(int targetType, String packageName, int positionPicked, + boolean isPinned) { FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED, /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), /* package_name = 2 */ packageName, /* instance_id = 3 */ getInstanceId().getId(), - /* position_picked = 4 */ positionPicked); + /* position_picked = 4 */ positionPicked, + /* is_pinned = 5 */ isPinned); } @Override diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java index c5b21ac4da90..e7f80a7f6071 100644 --- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java @@ -589,7 +589,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator MetricsLogger metricsLogger = new MetricsLogger(); LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED); log.setComponentName(mRankerServiceName); - log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed); + log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed ? 1 : 0); log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos); metricsLogger.write(log); } diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java index 957a6365739d..e56d92b48528 100644 --- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java +++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java @@ -91,7 +91,12 @@ public class UnlaunchableAppActivity extends Activity } else { builder.setPositiveButton(R.string.ok, null); } - builder.show(); + final AlertDialog dialog = builder.create(); + dialog.create(); + // Prevents screen overlay attack. + getWindow().setHideOverlayWindows(true); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); + dialog.show(); } private String getDialogTitle() { diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java index 983e0fe6144e..ffbf646a345c 100644 --- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java +++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java @@ -69,7 +69,8 @@ public class UiEventLoggerImpl implements UiEventLogger { /* event_id = 1 */ eventID, /* package_name = 2 */ packageName, /* instance_id = 3 */ 0, - /* position_picked = 4 */ position); + /* position_picked = 4 */ position, + /* is_pinned = 5 */ false); } } @@ -82,7 +83,8 @@ public class UiEventLoggerImpl implements UiEventLogger { /* event_id = 1 */ eventID, /* package_name = 2 */ packageName, /* instance_id = 3 */ instance.getId(), - /* position_picked = 4 */ position); + /* position_picked = 4 */ position, + /* is_pinned = 5 */ false); } else if ((eventID > 0)) { logWithPosition(event, uid, packageName, position); } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1db4bbba9ad5..ea5797d752d7 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1262,7 +1262,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) { + if (forceConsumingNavBar && !hideNavigation && !mDrawLegacyNavigationBarBackgroundHandled) { mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset); } else { mBackgroundInsets = Insets.NONE; diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 40d89db6165c..4e2526a281b3 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -23,6 +23,7 @@ import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethodSubtype; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.IInputContext; @@ -47,7 +48,8 @@ oneway interface IInputMethod { void unbindInput(); void startInput(in IBinder startInputToken, in IInputContext inputContext, - in EditorInfo attribute, boolean restarting, int navigationBarFlags); + in EditorInfo attribute, boolean restarting, int navigationBarFlags, + in ImeOnBackInvokedDispatcher imeDispatcher); void onNavButtonFlagsChanged(int navButtonFlags); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index d7bb2cb10b8c..315776045afe 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -20,6 +20,7 @@ import android.os.ResultReceiver; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.EditorInfo; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.inputmethod.InputBindResult; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; @@ -57,7 +58,7 @@ interface IInputMethodManager { /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, in EditorInfo attribute, in IInputContext inputContext, in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, - int unverifiedTargetSdkVersion); + int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index b6fbe206a262..f24c66695052 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1264,6 +1264,12 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, size_t numPositionMasks = 0; size_t numIndexMasks = 0; + int audioFormat = audioFormatFromNative(nAudioProfile->format); + if (audioFormat == ENCODING_INVALID) { + ALOGW("Unknown native audio format for JAVA API: %u", nAudioProfile->format); + return AUDIO_JAVA_BAD_VALUE; + } + // count up how many masks are positional and indexed for (size_t index = 0; index < nAudioProfile->num_channel_masks; index++) { const audio_channel_mask_t mask = nAudioProfile->channel_masks[index]; @@ -1306,10 +1312,9 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type); } - *jAudioProfile = - env->NewObject(gAudioProfileClass, gAudioProfileCstor, - audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(), - jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType); + *jAudioProfile = env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, + jSamplingRates.get(), jChannelMasks.get(), + jChannelIndexMasks.get(), encapsulationType); if (*jAudioProfile == nullptr) { return AUDIO_JAVA_ERROR; @@ -1368,6 +1373,10 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, jobject jAudioProfile = nullptr; jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], useInMask); + if (jStatus == AUDIO_JAVA_BAD_VALUE) { + // skipping Java layer unsupported audio formats + continue; + } if (jStatus != NO_ERROR) { jStatus = (jint)AUDIO_JAVA_ERROR; goto exit; @@ -2406,8 +2415,13 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th goto exit; } for (size_t i = 0; i < numSurroundFormats; i++) { - jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, - audioFormatFromNative(surroundFormats[i])); + int audioFormat = audioFormatFromNative(surroundFormats[i]); + if (audioFormat == ENCODING_INVALID) { + // skipping Java layer unsupported audio formats + ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]); + continue; + } + jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat); jobject enabled = env->NewObject(gBooleanClass, gBooleanCstor, surroundFormatsEnabled[i]); env->CallObjectMethod(jSurroundFormats, gMapPut, surroundFormat, enabled); env->DeleteLocalRef(surroundFormat); @@ -2453,8 +2467,13 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo goto exit; } for (size_t i = 0; i < numSurroundFormats; i++) { - jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, - audioFormatFromNative(surroundFormats[i])); + int audioFormat = audioFormatFromNative(surroundFormats[i]); + if (audioFormat == ENCODING_INVALID) { + // skipping Java layer unsupported audio formats + ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]); + continue; + } + jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat); env->CallObjectMethod(jSurroundFormats, gArrayListMethods.add, surroundFormat); env->DeleteLocalRef(surroundFormat); } @@ -2919,6 +2938,10 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env for (const auto &audioProfile : audioProfiles) { jobject jAudioProfile; jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false); + if (jStatus == AUDIO_JAVA_BAD_VALUE) { + // skipping Java layer unsupported audio formats + continue; + } if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java index 2ecc26179964..0dca63847269 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java @@ -44,6 +44,7 @@ public class ChooserActivityLoggerFake implements ChooserActivityLogger { // share completed fields public int targetType; public int positionPicked; + public boolean isPinned; CallRecord(int atomId, UiEventLogger.UiEventEnum eventId, String packageName, InstanceId instanceId) { @@ -68,12 +69,13 @@ public class ChooserActivityLoggerFake implements ChooserActivityLogger { } CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType, - int positionPicked) { + int positionPicked, boolean isPinned) { this.atomId = atomId; this.packageName = packageName; this.instanceId = instanceId; this.targetType = targetType; this.positionPicked = positionPicked; + this.isPinned = isPinned; } } @@ -112,9 +114,11 @@ public class ChooserActivityLoggerFake implements ChooserActivityLogger { } @Override - public void logShareTargetSelected(int targetType, String packageName, int positionPicked) { + public void logShareTargetSelected(int targetType, String packageName, int positionPicked, + boolean isPinned) { mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(), - SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked)); + SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked, + isPinned)); } @Override diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 0e8388b7e8f3..60da2e8cba27 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2485,12 +2485,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "323235828": { - "message": "Delaying app transition for recents animation to finish", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "327461496": { "message": "Complete pause: %s", "level": "VERBOSE", @@ -2581,12 +2575,6 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowContainer.java" }, - "397105698": { - "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "397382873": { "message": "Moving to PAUSED: %s %s", "level": "VERBOSE", @@ -3115,6 +3103,12 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/LockTaskController.java" }, + "958338552": { + "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "959486822": { "message": "setSyncGroup #%d on %s", "level": "VERBOSE", diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java new file mode 100644 index 000000000000..4855ad0f7293 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.KeyDescriptor; + +import java.security.PrivateKey; +import java.security.interfaces.EdECKey; +import java.security.spec.NamedParameterSpec; + +/** + * EdEC private key (instance of {@link PrivateKey} and {@link EdECKey}) backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey { + public AndroidKeyStoreEdECPrivateKey( + @NonNull KeyDescriptor descriptor, long keyId, + @NonNull Authorization[] authorizations, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel securityLevel) { + super(descriptor, keyId, authorizations, algorithm, securityLevel); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java new file mode 100644 index 000000000000..642e08813291 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import java.math.BigInteger; +import java.security.interfaces.EdECPublicKey; +import java.security.spec.EdECPoint; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.Objects; + +/** + * {@link EdECPublicKey} backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey + implements EdECPublicKey { + /** + * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and + * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1. + * SEQUENCE (2 elem) + * SEQUENCE (1 elem) + * OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) + * as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3 + * BIT STRING (256 bit) as defined in + * https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 + */ + private static final byte[] DER_KEY_PREFIX = new byte[] { + 0x30, + 0x2a, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2b, + 0x65, + 0x70, + 0x03, + 0x21, + 0x00, + }; + private static final int ED25519_KEY_SIZE_BYTES = 32; + + private byte[] mEncodedKey; + private EdECPoint mPoint; + + public AndroidKeyStoreEdECPublicKey( + @NonNull KeyDescriptor descriptor, + @NonNull KeyMetadata metadata, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel iSecurityLevel, + @NonNull byte[] encodedKey) { + super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel); + mEncodedKey = encodedKey; + + int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey); + if (preambleLength == 0) { + throw new IllegalArgumentException("Key size is not correct size"); + } + + mPoint = pointFromKeyByteArray( + Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length)); + } + + @Override + AndroidKeyStorePrivateKey getPrivateKey() { + return new AndroidKeyStoreEdECPrivateKey( + getUserKeyDescriptor(), + getKeyIdDescriptor().nspace, + getAuthorizations(), + "EdDSA", + getSecurityLevel()); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } + + @Override + public EdECPoint getPoint() { + return mPoint; + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) { + return 0; + } + if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) { + return 0; + } + return preamble.length; + } + + private static EdECPoint pointFromKeyByteArray(byte[] coordinates) { + Objects.requireNonNull(coordinates); + + // Oddity of the key is the most-significant bit of the last byte. + boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0; + // Zero out the oddity bit. + coordinates[coordinates.length - 1] &= (byte) 0x7f; + // Representation of Y is in little-endian, according to rfc8032 section-3.1. + reverse(coordinates); + // The integer representing Y starts from the first bit in the coordinates array. + BigInteger y = new BigInteger(1, coordinates); + return new EdECPoint(isOdd, y); + } + + private static void reverse(byte[] coordinateArray) { + int start = 0; + int end = coordinateArray.length - 1; + while (start < end) { + byte tmp = coordinateArray[start]; + coordinateArray[start] = coordinateArray[end]; + coordinateArray[end] = tmp; + start++; + end--; + } + } + + @Override + public byte[] getEncoded() { + return mEncodedKey.clone(); + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index d31499e8b36d..0355628b8135 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -224,7 +224,6 @@ public class AndroidKeyStoreProvider extends Provider { String jcaKeyAlgorithm = publicKey.getAlgorithm(); - KeyStoreSecurityLevel securityLevel = iSecurityLevel; if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { return new AndroidKeyStoreECPublicKey(descriptor, metadata, iSecurityLevel, (ECPublicKey) publicKey); @@ -232,8 +231,9 @@ public class AndroidKeyStoreProvider extends Provider { return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, iSecurityLevel, (RSAPublicKey) publicKey); } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) { - //TODO(b/214203951) missing classes in conscrypt - throw new ProviderException("Curve " + ED25519_OID + " not supported yet"); + final byte[] publicKeyEncoded = publicKey.getEncoded(); + return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID, + iSecurityLevel, publicKeyEncoded); } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) { //TODO(b/214203951) missing classes in conscrypt throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet"); diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java new file mode 100644 index 000000000000..5bd5797859c9 --- /dev/null +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.math.BigInteger; +import java.util.Base64; + +@RunWith(AndroidJUnit4.class) +public class AndroidKeyStoreEdECPublicKeyTest { + private static KeyDescriptor descriptor() { + final KeyDescriptor keyDescriptor = new KeyDescriptor(); + keyDescriptor.alias = "key"; + keyDescriptor.blob = null; + keyDescriptor.domain = Domain.APP; + keyDescriptor.nspace = -1; + return keyDescriptor; + } + + private static KeyMetadata metadata(byte[] cert, byte[] certChain) { + KeyMetadata metadata = new KeyMetadata(); + metadata.authorizations = new Authorization[0]; + metadata.certificate = cert; + metadata.certificateChain = certChain; + metadata.key = descriptor(); + metadata.modificationTimeMs = 0; + metadata.keySecurityLevel = 1; + return metadata; + } + + @Mock + private KeyStoreSecurityLevel mKeystoreSecurityLevel; + + private static class EdECTestVector { + public final byte[] encodedKeyBytes; + public final boolean isOdd; + public final BigInteger yValue; + + EdECTestVector(String b64KeyBytes, boolean isOdd, String yValue) { + this.encodedKeyBytes = Base64.getDecoder().decode(b64KeyBytes); + this.isOdd = isOdd; + this.yValue = new BigInteger(yValue); + } + } + + private static final EdECTestVector[] ED_EC_TEST_VECTORS = new EdECTestVector[]{ + new EdECTestVector("MCowBQYDK2VwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo=", + false, + "19147682157189290216699341180089409126316261024914226007941553249095116672780" + ), + new EdECTestVector("MCowBQYDK2VwAyEA/0E1IRNzGj85Ot/TPeXqifkqTkdk4voleH0hIq59D9w=", + true, + "41640152188550647350742178040529506688513911269563908889464821205156322689535" + ), + new EdECTestVector("MCowBQYDK2VwAyEAunOvGuenetl9GQSXGVo5L3RIr4OOIpFIv/Zre8qTc/8=", + true, + "57647939198144376128225770417635248407428273266444593100194116168980378907578" + ), + new EdECTestVector("MCowBQYDK2VwAyEA2hHqaZ5IolswN1Yd58Y4hzhmUMCCqc4PW5A/SFLmTX8=", + false, + "57581368614046789120409806291852629847774713088410311752049592044694364885466" + ), + }; + + @Test + public void testParsingOfValidKeys() { + for (EdECTestVector testVector : ED_EC_TEST_VECTORS) { + AndroidKeyStoreEdECPublicKey pkey = new AndroidKeyStoreEdECPublicKey(descriptor(), + metadata(null, null), "EdDSA", mKeystoreSecurityLevel, + testVector.encodedKeyBytes); + + assertEquals(pkey.getPoint().isXOdd(), testVector.isOdd); + assertEquals(pkey.getPoint().getY(), testVector.yValue); + } + } + + @Test + public void testFailedParsingOfKeysWithDifferentOid() { + final byte[] testVectorWithIncorrectOid = Base64.getDecoder().decode( + "MCowBQYDLGVwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo="); + assertThrows("OID should be unrecognized", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectOid)); + } + + @Test + public void testFailedParsingOfKeysWithWrongSize() { + final byte[] testVectorWithIncorrectKeySize = Base64.getDecoder().decode( + "MCwwBQYDK2VwAyMADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSrOzg=="); + assertThrows("Key length should be invalid", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectKeySize)); + } +} + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 64017e176fc3..d04c34916256 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -40,6 +40,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM_SHELL), WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), + WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 91f9d2522397..d543aa742377 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -54,6 +54,9 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -68,6 +71,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; +import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -147,7 +151,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final int mDisplayId; private SplitLayout mSplitLayout; + private ValueAnimator mDividerFadeInAnimator; private boolean mDividerVisible; + private boolean mKeyguardShowing; private final SyncTransactionQueue mSyncQueue; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; @@ -404,6 +410,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.init(); // Set false to avoid record new bounds with old task still on top; mShouldUpdateRecents = false; + mIsDividerRemoteAnimating = true; final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction evictWct = new WindowContainerTransaction(); prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct); @@ -417,7 +424,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { - mIsDividerRemoteAnimating = true; RemoteAnimationTarget[] augmentedNonApps = new RemoteAnimationTarget[nonApps.length + 1]; for (int i = 0; i < nonApps.length; ++i) { @@ -494,8 +500,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // Using legacy transitions, so we can't use blast sync since it conflicts. mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); + mSyncQueue.runInSync(t -> { + setDividerVisibility(true, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); + }); } private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { @@ -510,10 +518,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); } else { mSyncQueue.queue(evictWct); - mSyncQueue.runInSync(t -> { - setDividerVisibility(true, t); - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - }); } } @@ -623,16 +627,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void onKeyguardVisibilityChanged(boolean showing) { + mKeyguardShowing = showing; if (!mMainStage.isActive()) { return; } - if (ENABLE_SHELL_TRANSITIONS) { - // Update divider visibility so it won't float on top of keyguard. - setDividerVisibility(!showing, null /* transaction */); - } - - if (!showing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { + if (!mKeyguardShowing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); @@ -643,7 +643,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); } + return; } + + setDividerVisibility(!mKeyguardShowing, null); } void onFinishedWakingUp() { @@ -727,6 +730,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setResizingSplits(false /* resizing */); t.setWindowCrop(mMainStage.mRootLeash, null) .setWindowCrop(mSideStage.mRootLeash, null); + setDividerVisibility(false, t); }); // Hide divider and reset its position. @@ -1055,8 +1059,31 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) { + if (visible == mDividerVisible) { + return; + } + + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Request to %s divider bar from %s.", TAG, + (visible ? "show" : "hide"), Debug.getCaller()); + + // Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard + // dismissing animation. + if (visible && mKeyguardShowing) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Defer showing divider bar due to keyguard showing.", TAG); + return; + } + mDividerVisible = visible; sendSplitVisibilityChanged(); + + if (mIsDividerRemoteAnimating) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to it's remote animating.", TAG); + return; + } + if (t != null) { applyDividerVisibility(t); } else { @@ -1066,15 +1093,56 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void applyDividerVisibility(SurfaceControl.Transaction t) { final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (mIsDividerRemoteAnimating || dividerLeash == null) return; + if (dividerLeash == null) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to divider leash not ready.", TAG); + return; + } + if (mIsDividerRemoteAnimating) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to it's remote animating.", TAG); + return; + } + + if (mDividerFadeInAnimator != null && mDividerFadeInAnimator.isRunning()) { + mDividerFadeInAnimator.cancel(); + } if (mDividerVisible) { - t.show(dividerLeash); - t.setAlpha(dividerLeash, 1); - t.setLayer(dividerLeash, Integer.MAX_VALUE); - t.setPosition(dividerLeash, - mSplitLayout.getRefDividerBounds().left, - mSplitLayout.getRefDividerBounds().top); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f); + mDividerFadeInAnimator.addUpdateListener(animation -> { + if (dividerLeash == null) { + mDividerFadeInAnimator.cancel(); + return; + } + transaction.setAlpha(dividerLeash, (float) animation.getAnimatedValue()); + transaction.apply(); + }); + mDividerFadeInAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (dividerLeash == null) { + mDividerFadeInAnimator.cancel(); + return; + } + transaction.show(dividerLeash); + transaction.setAlpha(dividerLeash, 0); + transaction.setLayer(dividerLeash, Integer.MAX_VALUE); + transaction.setPosition(dividerLeash, + mSplitLayout.getRefDividerBounds().left, + mSplitLayout.getRefDividerBounds().top); + transaction.apply(); + } + + @Override + public void onAnimationEnd(Animator animation) { + mTransactionPool.release(transaction); + mDividerFadeInAnimator = null; + } + }); + + mDividerFadeInAnimator.start(); } else { t.hide(dividerLeash); } @@ -1096,10 +1164,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.init(); prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - setDividerVisibility(true, t); - }); + mSyncQueue.runInSync(t -> + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { mShouldUpdateRecents = true; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt index cf4ea467a29b..41cd31aabf05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt @@ -37,7 +37,7 @@ class AppPairsHelper( val displayBounds = WindowUtils.displayBounds val secondaryAppBounds = Region.from(0, dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarFrameHeight) return secondaryAppBounds } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index a510d699387e..e2da1a4565c0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -171,7 +171,7 @@ class ResizeLegacySplitScreen( val bottomAppBounds = Region.from(0, dividerBounds.bottom - WindowUtils.dockedStackDividerInset, displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.bottom - WindowUtils.navigationBarFrameHeight) visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) .coversExactly(topAppBounds) visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent()) @@ -192,7 +192,7 @@ class ResizeLegacySplitScreen( val bottomAppBounds = Region.from(0, dividerBounds.bottom - WindowUtils.dockedStackDividerInset, displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.bottom - WindowUtils.navigationBarFrameHeight) visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) .coversExactly(topAppBounds) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index a55f737f2f25..ffaab652aa99 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -139,6 +139,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testLaunchToSide() { ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); @@ -173,6 +174,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testLaunchPair() { TransitionInfo info = createEnterPairInfo(); @@ -195,6 +197,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testMonitorInSplit() { enterSplit(); @@ -251,6 +254,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testEnterRecents() { enterSplit(); @@ -288,6 +292,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromBeingOccluded() { enterSplit(); @@ -325,6 +330,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromMultiWindowSupport() { enterSplit(); @@ -346,6 +352,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissSnap() { enterSplit(); @@ -370,6 +377,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromAppFinish() { enterSplit(); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 37cbf3017f0b..5e918641e49f 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -484,6 +484,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements if (deviceFilterPairs.isEmpty()) return; mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); + // No need to show user consent dialog if it is a singleDevice + // and isSkipPrompt(true) AssociationRequest. + // See AssociationRequestsProcessor#mayAssociateWithoutPrompt. + if (mRequest.isSkipPrompt()) { + mSingleDeviceSpinner.setVisibility(View.GONE); + onUserSelectedDevice(mSelectedDevice); + return; + } final String deviceName = mSelectedDevice.getDisplayName(); final Spanned title = getHtmlFromResources( diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml index 25f0771b2170..72b569f22d6c 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml @@ -25,7 +25,7 @@ android:fitsSystemWindows="true" android:outlineAmbientShadowColor="@android:color/transparent" android:outlineSpotShadowColor="@android:color/transparent" - android:background="?android:attr/colorPrimary" + android:background="@android:color/transparent" android:theme="@style/Theme.CollapsingToolbar.Settings"> <com.google.android.material.appbar.CollapsingToolbarLayout diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java index 72383fe59e7e..dbb4b5017e6b 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java @@ -20,6 +20,7 @@ import android.app.ActionBar; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -29,6 +30,7 @@ import android.widget.Toolbar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.coordinatorlayout.widget.CoordinatorLayout; @@ -41,6 +43,7 @@ import com.google.android.material.appbar.CollapsingToolbarLayout; * This widget is wrapping the collapsing toolbar and can be directly used by the * {@link AppCompatActivity}. */ +@RequiresApi(Build.VERSION_CODES.S) public class CollapsingCoordinatorLayout extends CoordinatorLayout { private static final String TAG = "CollapsingCoordinatorLayout"; private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f; diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index ac306361386e..6766cdd0beb6 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -40,6 +40,8 @@ public class FooterPreference extends Preference { static final int ORDER_FOOTER = Integer.MAX_VALUE - 1; @VisibleForTesting View.OnClickListener mLearnMoreListener; + @VisibleForTesting + int mIconVisibility = View.VISIBLE; private CharSequence mContentDescription; private CharSequence mLearnMoreText; private CharSequence mLearnMoreContentDescription; @@ -84,6 +86,9 @@ public class FooterPreference extends Preference { } else { learnMore.setVisibility(View.GONE); } + + View icon = holder.itemView.findViewById(R.id.icon_frame); + icon.setVisibility(mIconVisibility); } @Override @@ -165,6 +170,17 @@ public class FooterPreference extends Preference { } } + /** + * Set visibility of footer icon. + */ + public void setIconVisibility(int iconVisibility) { + if (mIconVisibility == iconVisibility) { + return; + } + mIconVisibility = iconVisibility; + notifyChanged(); + } + private void init() { setLayoutResource(R.layout.preference_footer); if (getIcon() == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 284da73efb6f..2f30baa79b9d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -234,5 +234,10 @@ public class RestrictedPreferenceHelper { if (!(mPreference instanceof RestrictedTopLevelPreference)) { mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps)); } + + if (mPreference instanceof PrimarySwitchPreference) { + ((PrimarySwitchPreference) mPreference) + .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps)); + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index fdb06072bbd1..6b9daa35f9ca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -863,6 +863,30 @@ public class ApplicationsState { } } + /** + * Activate session to enable a class that implements Callbacks to receive the callback. + */ + public void activateSession() { + synchronized (mEntriesMap) { + if (!mResumed) { + mResumed = true; + mSessionsChanged = true; + } + } + } + + /** + * Deactivate session to disable a class that implements Callbacks to get the callback. + */ + public void deactivateSession() { + synchronized (mEntriesMap) { + if (mResumed) { + mResumed = false; + mSessionsChanged = true; + } + } + } + public ArrayList<AppEntry> getAllApps() { synchronized (mEntriesMap) { return new ArrayList<>(mAppEntries); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 4ee21229e364..6919cf237853 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -1376,7 +1376,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> /** * Store the member devices that are in the same coordinated set. */ - public void setMemberDevice(CachedBluetoothDevice memberDevice) { + public void addMemberDevice(CachedBluetoothDevice memberDevice) { mMemberDevices.add(memberDevice); } @@ -1393,24 +1393,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> * device and member devices. * * @param prevMainDevice the previous Main device, it will be added into the member device set. - * @param newMainDevie the new Main device, it will be removed from the member device set. + * @param newMainDevice the new Main device, it will be removed from the member device set. */ public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice, - CachedBluetoothDevice newMainDevie) { + CachedBluetoothDevice newMainDevice) { // Backup from main device final BluetoothDevice tmpDevice = mDevice; final short tmpRssi = mRssi; final boolean tmpJustDiscovered = mJustDiscovered; // Set main device from sub device - mDevice = newMainDevie.mDevice; - mRssi = newMainDevie.mRssi; - mJustDiscovered = newMainDevie.mJustDiscovered; - setMemberDevice(prevMainDevice); - mMemberDevices.remove(newMainDevie); + mDevice = newMainDevice.mDevice; + mRssi = newMainDevice.mRssi; + mJustDiscovered = newMainDevice.mJustDiscovered; + addMemberDevice(prevMainDevice); + mMemberDevices.remove(newMainDevice); // Set sub device from backup - newMainDevie.mDevice = tmpDevice; - newMainDevie.mRssi = tmpRssi; - newMainDevie.mJustDiscovered = tmpJustDiscovered; + newMainDevice.mDevice = tmpDevice; + newMainDevice.mRssi = tmpRssi; + newMainDevice.mJustDiscovered = tmpJustDiscovered; fetchActiveDevices(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index cc56a212aea1..89e10c4b5e11 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -85,7 +85,7 @@ public class CsipDeviceManager { // Once there is other devices with the same groupId, to add new device as member // devices. if (CsipDevice != null) { - CsipDevice.setMemberDevice(newDevice); + CsipDevice.addMemberDevice(newDevice); newDevice.setName(CsipDevice.getName()); return true; } @@ -148,7 +148,7 @@ public class CsipDeviceManager { log("onGroupIdChanged: removed from UI device =" + cachedDevice + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex); - mainDevice.setMemberDevice(cachedDevice); + mainDevice.addMemberDevice(cachedDevice); mCachedDevices.remove(i); mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice); break; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 298ee90d311d..bef1d9cbcde1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -40,7 +40,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.Collection; -import java.util.HashMap; import java.util.Map; @RunWith(RobolectricTestRunner.class) @@ -503,8 +502,8 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3); - cachedDevice1.setMemberDevice(cachedDevice2); - cachedDevice1.setMemberDevice(cachedDevice3); + cachedDevice1.addMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice3); assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2); assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3); @@ -524,7 +523,7 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); cachedDevice1.setGroupId(1); cachedDevice2.setGroupId(1); - cachedDevice1.setMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice2); // Call onDeviceUnpaired for the one in mCachedDevices. mCachedDeviceManager.onDeviceUnpaired(cachedDevice1); @@ -541,7 +540,7 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); cachedDevice1.setGroupId(1); cachedDevice2.setGroupId(1); - cachedDevice1.setMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice2); // Call onDeviceUnpaired for the one in mCachedDevices. mCachedDeviceManager.onDeviceUnpaired(cachedDevice2); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index 61a28aab061f..9abb27e68398 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.view.LayoutInflater; +import android.view.View; import android.widget.TextView; import androidx.preference.PreferenceViewHolder; @@ -61,7 +62,7 @@ public class FooterPreferenceTest { mFooterPreference.onBindViewHolder(holder); assertThat(((TextView) holder.findViewById( - R.id.settingslib_learn_more)).getText().toString()) + R.id.settingslib_learn_more)).getText().toString()) .isEqualTo("Custom learn more"); } @@ -86,4 +87,11 @@ public class FooterPreferenceTest { assertThat(mFooterPreference.mLearnMoreListener).isNotNull(); } + + @Test + public void setIconVisibility_shouldReturnSameVisibilityType() { + mFooterPreference.setIconVisibility(View.GONE); + + assertThat(mFooterPreference.mIconVisibility).isEqualTo(View.GONE); + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 3029781f3e99..5eaf553a2047 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -120,6 +120,9 @@ public class SecureSettings { Settings.Secure.ACTIVE_UNLOCK_ON_WAKE, Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, + Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, Settings.Secure.VR_DISPLAY_MODE, Settings.Secure.NOTIFICATION_BADGING, Settings.Secure.NOTIFICATION_DISMISS_RTL, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index a4da49713f87..9ee7b654046f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -18,6 +18,7 @@ package android.provider.settings.validators; import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR; @@ -176,6 +177,10 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + ANY_STRING_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml index 528f603c6a83..fd0623812745 100644 --- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml +++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> - <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2022 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -15,88 +14,83 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<androidx.core.widget.NestedScrollView + +<com.android.internal.widget.AlertDialogLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@*android:id/parentPanel" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:gravity="center_horizontal|top" + android:orientation="vertical" + android:paddingTop="@dimen/dialog_top_padding" +> + + <include layout="@layout/alert_dialog_title_systemui" /> - <com.android.internal.widget.AlertDialogLayout - android:id="@*android:id/parentPanel" + <FrameLayout + android:id="@*android:id/contentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_horizontal|top" - android:orientation="vertical" - android:paddingTop="@dimen/dialog_top_padding" - > + android:minHeight="48dp" + android:paddingStart="@dimen/dialog_side_padding" + android:paddingEnd="@dimen/dialog_side_padding" + > - <include layout="@layout/alert_dialog_title_systemui" /> - - <FrameLayout - android:id="@*android:id/contentPanel" + <ScrollView + android:id="@*android:id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="48dp" - android:paddingStart="@dimen/dialog_side_padding" - android:paddingEnd="@dimen/dialog_side_padding" - > + android:clipToPadding="false"> - <ScrollView - android:id="@*android:id/scrollView" + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:clipToPadding="false"> + android:orientation="vertical"> - <LinearLayout + <Space + android:id="@*android:id/textSpacerNoTitle" + android:visibility="gone" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> + android:layout_height="0dp" /> - <Space - android:id="@*android:id/textSpacerNoTitle" - android:visibility="gone" - android:layout_width="match_parent" - android:layout_height="0dp" /> + <TextView + android:id="@*android:id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/TextAppearance.Dialog.Body.Message" /> - <TextView - android:id="@*android:id/message" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/TextAppearance.Dialog.Body.Message" /> + <Space + android:id="@*android:id/textSpacerNoButtons" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="6dp" /> + </LinearLayout> + </ScrollView> + </FrameLayout> - <Space - android:id="@*android:id/textSpacerNoButtons" - android:visibility="gone" - android:layout_width="match_parent" - android:layout_height="6dp" /> - </LinearLayout> - </ScrollView> - </FrameLayout> + <FrameLayout + android:id="@*android:id/customPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="48dp" + android:paddingStart="@dimen/dialog_side_padding" + android:paddingEnd="@dimen/dialog_side_padding" + > <FrameLayout - android:id="@*android:id/customPanel" + android:id="@*android:id/custom" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="48dp" - android:paddingStart="@dimen/dialog_side_padding" - android:paddingEnd="@dimen/dialog_side_padding" - > + android:layout_height="wrap_content" /> + </FrameLayout> - <FrameLayout - android:id="@*android:id/custom" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - </FrameLayout> - - <FrameLayout + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="@dimen/dialog_side_padding" + android:paddingEnd="@dimen/dialog_side_padding"> + <include android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="@dimen/dialog_side_padding" - android:paddingEnd="@dimen/dialog_side_padding"> - <include - android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/alert_dialog_button_bar_systemui" /> - </FrameLayout> - </com.android.internal.widget.AlertDialogLayout> - -</androidx.core.widget.NestedScrollView>
\ No newline at end of file + layout="@layout/alert_dialog_button_bar_systemui" /> + </FrameLayout> +</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml new file mode 100644 index 000000000000..71bb938dfdf4 --- /dev/null +++ b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<androidx.core.widget.NestedScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <include layout="@layout/alert_dialog_systemui" /> + +</androidx.core.widget.NestedScrollView>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f9e73ec3a76f..0c25f5473416 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -380,7 +380,7 @@ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item> <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item> <item name="android:colorBackground">?androidprv:attr/colorSurface</item> - <item name="android:alertDialogStyle">@style/AlertDialogStyle</item> + <item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item> <item name="android:buttonBarStyle">@style/ButtonBarStyle</item> <item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item> </style> @@ -389,6 +389,10 @@ <item name="android:layout">@layout/alert_dialog_systemui</item> </style> + <style name="ScrollableAlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault"> + <item name="android:layout">@layout/scrollable_alert_dialog_systemui</item> + </style> + <style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog"> <item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item> <item name="android:paddingBottom">@dimen/dialog_bottom_padding</item> @@ -994,6 +998,7 @@ <style name="Theme.SystemUI.Dialog.Cast"> <item name="android:textAppearanceMedium">@style/TextAppearance.CastItem</item> + <item name="android:alertDialogStyle">@style/AlertDialogStyle</item> </style> <!-- ************************************************************************************* --> diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt index f195d2094d6a..38fa35453418 100644 --- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt +++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt @@ -16,14 +16,20 @@ package com.android.keyguard +import android.annotation.IntDef import android.content.ContentResolver import android.database.ContentObserver +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT import android.net.Uri import android.os.Handler import android.os.UserHandle import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE +import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton @@ -44,6 +50,20 @@ class ActiveUnlockConfig @Inject constructor( dumpManager: DumpManager ) : Dumpable { + companion object { + const val TAG = "ActiveUnlockConfig" + + const val BIOMETRIC_TYPE_NONE = 0 + const val BIOMETRIC_TYPE_ANY_FACE = 1 + const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2 + const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3 + } + + @Retention(AnnotationRetention.SOURCE) + @IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT, + BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT) + annotation class BiometricType + /** * Indicates the origin for an active unlock request. */ @@ -51,35 +71,50 @@ class ActiveUnlockConfig @Inject constructor( WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT } + var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null private var requestActiveUnlockOnWakeup = false private var requestActiveUnlockOnUnlockIntent = false private var requestActiveUnlockOnBioFail = false + private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT) + private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>() + private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE) + private val settingsObserver = object : ContentObserver(handler) { - private val wakeUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE) - private val unlockIntentUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT) - private val bioFailUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL) + private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE) + private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT) + private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL) + private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS) + private val faceAcquireInfoUri = + secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO) + private val unlockIntentWhenBiometricEnrolledUri = + secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED) fun register() { - contentResolver.registerContentObserver( - wakeUri, - false, - this, - UserHandle.USER_ALL) - contentResolver.registerContentObserver( - unlockIntentUri, - false, - this, - UserHandle.USER_ALL) - contentResolver.registerContentObserver( - bioFailUri, - false, - this, - UserHandle.USER_ALL) + registerUri( + listOf( + wakeUri, + unlockIntentUri, + bioFailUri, + faceErrorsUri, + faceAcquireInfoUri, + unlockIntentWhenBiometricEnrolledUri + ) + ) onChange(true, ArrayList(), 0, getCurrentUser()) } + private fun registerUri(uris: Collection<Uri>) { + for (uri in uris) { + contentResolver.registerContentObserver( + uri, + false, + this, + UserHandle.USER_ALL) + } + } + override fun onChange( selfChange: Boolean, uris: Collection<Uri>, @@ -104,6 +139,55 @@ class ActiveUnlockConfig @Inject constructor( requestActiveUnlockOnBioFail = secureSettings.getIntForUser( ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1 } + + if (selfChange || uris.contains(faceErrorsUri)) { + processStringArray( + secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS, + getCurrentUser()), + faceErrorsToTriggerBiometricFailOn, + setOf(FACE_ERROR_TIMEOUT)) + } + + if (selfChange || uris.contains(faceAcquireInfoUri)) { + processStringArray( + secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + getCurrentUser()), + faceAcquireInfoToTriggerBiometricFailOn, + setOf<Int>()) + } + + if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) { + processStringArray( + secureSettings.getStringForUser( + ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + getCurrentUser()), + onUnlockIntentWhenBiometricEnrolled, + setOf(BIOMETRIC_TYPE_NONE)) + } + } + + /** + * Convert a pipe-separated set of integers into a set of ints. + * @param stringSetting expected input are integers delineated by a pipe. For example, + * it may look something like this: "1|5|3". + * @param out updates the "out" Set will the integers between the pipes. + * @param default If stringSetting is null, "out" will be populated with values in "default" + */ + private fun processStringArray( + stringSetting: String?, + out: MutableSet<Int>, + default: Set<Int> + ) { + out.clear() + stringSetting?.let { + for (code: String in stringSetting.split("|")) { + try { + out.add(code.toInt()) + } catch (e: NumberFormatException) { + Log.e(TAG, "Passed an invalid setting=$code") + } + } + } ?: out.addAll(default) } } @@ -113,6 +197,30 @@ class ActiveUnlockConfig @Inject constructor( } /** + * If any active unlock triggers are enabled. + */ + fun isActiveUnlockEnabled(): Boolean { + return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent || + requestActiveUnlockOnBioFail + } + + /** + * Whether the face error code from {@link BiometricFaceConstants} should trigger + * active unlock on biometric failure. + */ + fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean { + return faceErrorsToTriggerBiometricFailOn.contains(errorCode) + } + + /** + * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger + * active unlock on biometric failure. + */ + fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean { + return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo) + } + + /** * Whether to trigger active unlock based on where the request is coming from and * the current settings. */ @@ -121,7 +229,8 @@ class ActiveUnlockConfig @Inject constructor( ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT -> - requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup + requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup || + (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()) ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL -> requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent || @@ -131,17 +240,55 @@ class ActiveUnlockConfig @Inject constructor( } } - /** - * If any active unlock triggers are enabled. - */ - fun isActiveUnlockEnabled(): Boolean { - return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent || - requestActiveUnlockOnBioFail + private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean { + if (!requestActiveUnlockOnBioFail) { + return false + } + + keyguardUpdateMonitor?.let { + val anyFaceEnrolled = it.isFaceEnrolled + val anyFingerprintEnrolled = + it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser()) + val udfpsEnrolled = it.isUdfpsEnrolled + + if (!anyFaceEnrolled && !anyFingerprintEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE) + } + + if (!anyFaceEnrolled && anyFingerprintEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains( + BIOMETRIC_TYPE_ANY_FINGERPRINT) || + (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains( + BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)) + } + + if (!anyFingerprintEnrolled && anyFaceEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE) + } + } + + return false } override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println("Settings:") pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup") pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent") pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail") + pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" + + "$onUnlockIntentWhenBiometricEnrolled") + pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn") + pw.println(" requestActiveUnlockOnFaceAcquireInfo=" + + "$faceAcquireInfoToTriggerBiometricFailOn") + + pw.println("Current state:") + keyguardUpdateMonitor?.let { + pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" + + "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}") + pw.println(" faceEnrolled=${it.isFaceEnrolled}") + pw.println(" fpEnrolled=${ + it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}") + pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}") + } ?: pw.println(" keyguardUpdateMonitor is uninitialized") } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index bbe9a362b1fa..121ac299ec5b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -56,7 +56,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; -import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -1615,7 +1614,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mKeyguardBypassController.setUserHasDeviceEntryIntent(false); } - if (errMsgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) { + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) { requestActiveUnlock( ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL, "faceError-" + errMsgId); @@ -1625,6 +1624,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationAcquired(int acquireInfo) { handleFaceAcquired(acquireInfo); + + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + acquireInfo)) { + requestActiveUnlock( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL, + "faceAcquireInfo-" + acquireInfo); + } } }; @@ -1639,6 +1645,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mFingerprintLockedOut; private boolean mFingerprintLockedOutPermanent; private boolean mFaceLockedOutPermanent; + private HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>(); private TelephonyManager mTelephonyManager; /** @@ -1889,6 +1896,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab dumpManager.registerDumpable(getClass().getName(), this); mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); mActiveUnlockConfig = activeUnlockConfiguration; + mActiveUnlockConfig.setKeyguardUpdateMonitor(this); mHandler = new Handler(mainLooper) { @Override @@ -2329,7 +2337,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (shouldTriggerActiveUnlock()) { - if (DEBUG) { + if (DEBUG_ACTIVE_UNLOCK) { Log.d("ActiveUnlock", "initiate active unlock triggerReason=" + reason); } mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser()); @@ -2359,7 +2367,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (allowRequest && shouldTriggerActiveUnlock()) { - if (DEBUG) { + if (DEBUG_ACTIVE_UNLOCK) { Log.d("ActiveUnlock", "reportUserRequestedUnlock" + " origin=" + requestOrigin.name() + " reason=" + reason @@ -2777,8 +2785,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private boolean isUnlockWithFingerprintPossible(int userId) { - return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId) - && mFpm.hasEnrolledTemplates(userId); + mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected() + && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId)); + return mIsUnlockWithFingerprintPossible.get(userId); + } + + /** + * Cached value for whether fingerprint is enrolled and possible to use for authentication. + * Note: checking fingerprint enrollment directly with the AuthController requires an IPC. + */ + public boolean getCachedIsUnlockWithFingerprintPossible(int userId) { + return mIsUnlockWithFingerprintPossible.get(userId); } private boolean isUnlockWithFacePossible(int userId) { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index e1913657b7cc..fdde40296511 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -55,7 +55,7 @@ public class LockIconView extends FrameLayout implements Dumpable { @NonNull private final RectF mSensorRect; @NonNull private PointF mLockIconCenter = new PointF(0f, 0f); - private int mRadius; + private float mRadius; private int mLockIconPadding; private ImageView mLockIcon; @@ -126,7 +126,7 @@ public class LockIconView extends FrameLayout implements Dumpable { * Set the location of the lock icon. */ @VisibleForTesting - public void setCenterLocation(@NonNull PointF center, int radius, int drawablePadding) { + public void setCenterLocation(@NonNull PointF center, float radius, int drawablePadding) { mLockIconCenter = center; mRadius = radius; mLockIconPadding = drawablePadding; diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 2b217f02e834..d79b1454514e 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -188,6 +188,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme protected void onViewAttached() { updateIsUdfpsEnrolled(); updateConfiguration(); + updateLockIconLocation(); updateKeyguardShowing(); mUserUnlockedWithBiometric = false; @@ -340,20 +341,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mHeightPixels = bounds.bottom; mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor()); - mUnlockedLabel = mView.getContext().getResources().getString( R.string.accessibility_unlock_button); mLockedLabel = mView.getContext() .getResources().getString(R.string.accessibility_lock_icon); - - updateLockIconLocation(); } private void updateLockIconLocation() { if (mUdfpsSupported) { + final int defaultPaddingPx = + getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); + mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor()); mView.setCenterLocation(mAuthController.getUdfpsLocation(), mAuthController.getUdfpsRadius(), mScaledPaddingPx); } else { @@ -362,8 +360,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx), sLockIconRadiusPx, mScaledPaddingPx); } - - mView.getHitRect(mSensorTouchLocation); } @Override @@ -386,6 +382,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); + pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); if (mView != null) { mView.dump(pw, args); @@ -672,6 +669,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } private boolean inLockIconArea(MotionEvent event) { + mView.getHitRect(mSensorTouchLocation); return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) && mView.getVisibility() == View.VISIBLE; } @@ -692,6 +690,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mExecutor.execute(() -> { updateIsUdfpsEnrolled(); updateConfiguration(); + updateLockIconLocation(); }); } @@ -705,6 +704,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme public void onEnrollmentsChanged() { updateUdfpsConfig(); } + + @Override + public void onUdfpsLocationChanged() { + updateLockIconLocation(); + } }; private final View.OnClickListener mA11yClickListener = v -> onLongPress(); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index cbce854e4a71..dd312186afee 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -47,6 +47,7 @@ import android.hardware.graphics.common.AlphaInterpretation; import android.hardware.graphics.common.DisplayDecorationSupport; import android.os.Handler; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.provider.Settings.Secure; import android.util.DisplayUtils; @@ -1067,15 +1068,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void updateLayoutParams() { - if (mOverlays == null) { - return; + //ToDo: We should skip unnecessary call to update view layout. + Trace.beginSection("ScreenDecorations#updateLayoutParams"); + if (mScreenDecorHwcWindow != null) { + mWindowManager.updateViewLayout(mScreenDecorHwcWindow, getHwcWindowLayoutParams()); } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; + + if (mOverlays != null) { + for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { + if (mOverlays[i] == null) { + continue; + } + mWindowManager.updateViewLayout( + mOverlays[i].getRootView(), getWindowLayoutParams(i)); } - mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i)); } + Trace.endSection(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 932489372872..75339aaa843d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -79,6 +79,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import javax.inject.Inject; @@ -446,11 +447,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba /** * @return the radius of UDFPS on the screen in pixels */ - public int getUdfpsRadius() { + public float getUdfpsRadius() { if (mUdfpsController == null || mUdfpsBounds == null) { return -1; } - return mUdfpsBounds.height() / 2; + return mUdfpsBounds.height() / 2f; } /** @@ -634,11 +635,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba displayInfo.getNaturalHeight()); final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0); + final Rect previousUdfpsBounds = mUdfpsBounds; mUdfpsBounds = udfpsProp.getLocation().getRect(); mUdfpsBounds.scale(scaleFactor); mUdfpsController.updateOverlayParams(udfpsProp.sensorId, new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation)); + if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) { + for (Callback cb : mCallbacks) { + cb.onUdfpsLocationChanged(); + } + } } } @@ -1054,5 +1061,10 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba * Called when the biometric prompt is no longer showing. */ default void onBiometricPromptDismissed() {} + + /** + * The location in pixels can change due to resolution changes. + */ + default void onUdfpsLocationChanged() {} } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index d9aa1bae3c69..86e501670440 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -335,15 +335,17 @@ class AuthRippleController @Inject constructor( updateSensorLocation() } - override fun onEnrollmentsChanged() { + override fun onUdfpsLocationChanged() { + updateUdfpsDependentParams() + updateSensorLocation() } } private fun updateUdfpsDependentParams() { authController.udfpsProps?.let { if (it.size > 0) { - udfpsRadius = it[0].location.sensorRadius.toFloat() udfpsController = udfpsControllerProvider.get() + udfpsRadius = authController.udfpsRadius if (mView.isAttachedToWindow) { udfpsController?.addCallback(udfpsControllerCallback) diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 74044e2c2eb8..1cc5df5d04cf 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -39,7 +39,6 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; @@ -94,7 +93,6 @@ public class DozeTriggers implements DozeMachine.Part { private final AuthController mAuthController; private final DelayableExecutor mMainExecutor; private final KeyguardStateController mKeyguardStateController; - private final BatteryController mBatteryController; private final UiEventLogger mUiEventLogger; private final DevicePostureController mDevicePostureController; @@ -186,8 +184,7 @@ public class DozeTriggers implements DozeMachine.Part { @Main DelayableExecutor mainExecutor, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - DevicePostureController devicePostureController, - BatteryController batteryController) { + DevicePostureController devicePostureController) { mContext = context; mDozeHost = dozeHost; mConfig = config; @@ -208,7 +205,6 @@ public class DozeTriggers implements DozeMachine.Part { mMainExecutor = mainExecutor; mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; - mBatteryController = batteryController; } private final DevicePostureController.Callback mDevicePostureCallback = posture -> { @@ -320,12 +316,7 @@ public class DozeTriggers implements DozeMachine.Part { gentleWakeUp(pulseReason); } else if (isPickup) { if (shouldDropPickupEvent()) { - mDozeLog.traceSensorEventDropped( - pulseReason, - "keyguardOccluded=" - + mKeyguardStateController.isOccluded() - + " pluggedInWireless=" - + mBatteryController.isPluggedInWireless()); + mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded"); return; } gentleWakeUp(pulseReason); @@ -356,7 +347,7 @@ public class DozeTriggers implements DozeMachine.Part { } private boolean shouldDropPickupEvent() { - return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless(); + return mKeyguardStateController.isOccluded(); } private void gentleWakeUp(int reason) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 82f935c7193f..626e185dec15 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -651,6 +651,10 @@ public class MediaControlPanel { mIsArtworkBound = isArtworkBound; } + // Scrim bounds are set manually so it scales as expected + albumView.getForeground().setBounds(0, 0, + Math.max(width, height), Math.max(width, height)); + // Transition Colors to current color scheme mColorSchemeTransition.updateColorScheme(colorScheme, mIsArtworkBound); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 83970dc15e61..629aa0317d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -220,7 +220,7 @@ public class KeyguardClockPositionAlgorithm { } } - public float getMinStackScrollerPadding() { + public float getLockscreenMinStackScrollerPadding() { if (mBypassEnabled) { return mUnlockedStackScrollerPadding; } else if (mIsSplitShade) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 985df423d744..74b9c71d2198 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -24,6 +24,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; +import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -325,6 +326,11 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; + /** + * The top padding from where notification should start in lockscreen. + * Should be static also during animations and should match the Y of the first notification. + */ + private float mKeyguardNotificationTopPadding; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -936,7 +942,7 @@ public class NotificationPanelViewController extends PanelViewController { // the launcher icons animation starts, so use that as our // duration. .setDuration(unlockAnimationStartDelay) - .setInterpolator(EMPHASIZED_DECELERATE) + .setInterpolator(EMPHASIZED_ACCELERATE) .withEndAction(() -> { instantCollapse(); mView.setAlpha(1f); @@ -1513,7 +1519,10 @@ public class NotificationPanelViewController extends PanelViewController { */ @VisibleForTesting float getSpaceForLockscreenNotifications() { - float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); + float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding() + // getMinStackScrollerPadding is from the top of the screen, + // but we need it from the top of the NSSL. + - mNotificationStackScrollLayoutController.getTop(); // Space between bottom of notifications and top of lock icon or udfps background. float lockIconPadding = mLockIconViewController.getTop(); @@ -1525,11 +1534,15 @@ public class NotificationPanelViewController extends PanelViewController { float bottomPadding = Math.max(lockIconPadding, Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)); + mKeyguardNotificationBottomPadding = bottomPadding; + mKeyguardNotificationTopPadding = staticTopPadding; + // To debug the available space, enable debug lines in this class. If you change how the + // available space is calculated, please also update those lines. float availableSpace = mNotificationStackScrollLayoutController.getHeight() - - topPadding + - staticTopPadding - bottomPadding; return availableSpace; } @@ -4923,6 +4936,20 @@ public class NotificationPanelViewController extends PanelViewController { drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY, "mLockIconViewController.getTop()"); + if (mKeyguardShowing) { + // Notifications have the space between those two lines. + drawDebugInfo(canvas, + mNotificationStackScrollLayoutController.getTop() + + (int) mKeyguardNotificationTopPadding, + Color.RED, + "NSSL.getTop() + mKeyguardNotificationTopPadding"); + + drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom() - + (int) mKeyguardNotificationBottomPadding, + Color.RED, + "NSSL.getBottom() - mKeyguardNotificationBottomPadding"); + } + mDebugPaint.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint); diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index acff8712e92e..ebdddbf66091 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -180,7 +180,7 @@ public class QuickAccessWalletController { int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); GetWalletCardsRequest request = new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); - mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever); + mQuickAccessWalletClient.getWalletCards(mCallbackExecutor, request, cardsRetriever); } /** diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt index 747649006b45..39cc34bb7e26 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt @@ -18,6 +18,7 @@ package com.android.keyguard import android.content.ContentResolver import android.database.ContentObserver +import android.hardware.biometrics.BiometricFaceConstants import android.net.Uri import android.os.Handler import android.os.UserHandle @@ -44,18 +45,20 @@ class ActiveUnlockConfigTest : SysuiTestCase() { private val fakeWakeUri = Uri.Builder().appendPath("wake").build() private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build() private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build() + private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build() + private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build() + private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build() @Mock private lateinit var secureSettings: SecureSettings - @Mock private lateinit var contentResolver: ContentResolver - @Mock private lateinit var handler: Handler - @Mock private lateinit var dumpManager: DumpManager + @Mock + private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> @@ -72,6 +75,13 @@ class ActiveUnlockConfigTest : SysuiTestCase() { .thenReturn(fakeUnlockIntentUri) `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) .thenReturn(fakeBioFailUri) + `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS)) + .thenReturn(fakeFaceErrorsUri) + `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)) + .thenReturn(fakeFaceAcquiredUri) + `when`(secureSettings.getUriFor( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)) + .thenReturn(fakeUnlockIntentBioEnroll) activeUnlockConfig = ActiveUnlockConfig( handler, @@ -99,12 +109,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN unlock on wake is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeWakeUri), - 0, - 0 - ) + updateSetting(fakeWakeUri) // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure assertTrue( @@ -134,12 +139,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN unlock on biometric failed is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeUnlockIntentUri), - 0, - 0 - ) + updateSetting(fakeUnlockIntentUri) // THEN active unlock triggers allowed on: biometric failure ONLY assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( @@ -154,19 +154,19 @@ class ActiveUnlockConfigTest : SysuiTestCase() { fun testOnBioFailSettingChanged() { verifyRegisterSettingObserver() - // GIVEN no active unlock settings enabled + // GIVEN no active unlock settings enabled and triggering unlock intent on biometric + // enrollment setting is disabled (empty string is disabled, null would use the default) + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn("") + updateSetting(fakeUnlockIntentBioEnroll) assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL)) // WHEN unlock on biometric failed is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeBioFailUri), - 0, - 0 - ) + updateSetting(fakeBioFailUri) // THEN active unlock triggers allowed on: biometric failure ONLY assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( @@ -177,21 +177,146 @@ class ActiveUnlockConfigTest : SysuiTestCase() { ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL)) } - private fun verifyRegisterSettingObserver() { - verify(contentResolver).registerContentObserver( - eq(fakeWakeUri), - eq(false), - capture(settingsObserverCaptor), - eq(UserHandle.USER_ALL)) + @Test + fun testFaceErrorSettingsChanged() { + verifyRegisterSettingObserver() - verify(contentResolver).registerContentObserver( - eq(fakeUnlockIntentUri), - eq(false), - capture(settingsObserverCaptor), - eq(UserHandle.USER_ALL)) + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // WHEN face error timeout (3), allow trigger active unlock + `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, + 0)).thenReturn("3") + updateSetting(fakeFaceAcquiredUri) + + // THEN active unlock triggers allowed on error TIMEOUT + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( + BiometricFaceConstants.FACE_ERROR_TIMEOUT)) + + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( + BiometricFaceConstants.FACE_ERROR_CANCELED)) + } + + @Test + fun testFaceAcquiredSettingsChanged() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger + `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + 0)).thenReturn( + "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" + + "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}") + updateSetting(fakeFaceAcquiredUri) + + // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED)) + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED)) + + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_GOOD)) + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED)) + } + + @Test + fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // GIVEN fingerprint and face are NOT enrolled + activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + + // WHEN unlock intent is allowed when NO biometrics are enrolled (0) + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}") + updateSetting(fakeUnlockIntentBioEnroll) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + } + + @Test + fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // GIVEN fingerprint and face are both enrolled + activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + + // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs + // are enrolled + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn( + "${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" + + "|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}") + updateSetting(fakeUnlockIntentBioEnroll) + + // THEN active unlock triggers NOT allowed on unlock intent + assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + + // WHEN fingerprint ONLY enrolled + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + + // WHEN face ONLY enrolled + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + } + + private fun updateSetting(uri: Uri) { + settingsObserverCaptor.value.onChange( + false, + listOf(uri), + 0, + 0 /* flags */ + ) + } + + private fun verifyRegisterSettingObserver() { + verifyRegisterSettingObserver(fakeWakeUri) + verifyRegisterSettingObserver(fakeUnlockIntentUri) + verifyRegisterSettingObserver(fakeBioFailUri) + verifyRegisterSettingObserver(fakeFaceErrorsUri) + verifyRegisterSettingObserver(fakeFaceAcquiredUri) + verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll) + } + private fun verifyRegisterSettingObserver(uri: Uri) { verify(contentResolver).registerContentObserver( - eq(fakeBioFailUri), + eq(uri), eq(false), capture(settingsObserverCaptor), eq(UserHandle.USER_ALL)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index ae387e86de9d..4eeb4acf18b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -45,7 +45,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -91,8 +90,6 @@ public class DozeTriggersTest extends SysuiTestCase { private KeyguardStateController mKeyguardStateController; @Mock private DevicePostureController mDevicePostureController; - @Mock - private BatteryController mBatteryController; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -125,7 +122,7 @@ public class DozeTriggersTest extends SysuiTestCase { asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController, - mDevicePostureController, mBatteryController); + mDevicePostureController); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -233,9 +230,7 @@ public class DozeTriggersTest extends SysuiTestCase { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); // WHEN the pick up gesture is triggered and keyguard isn't occluded - // and device isn't on a wireless charger when(mKeyguardStateController.isOccluded()).thenReturn(false); - when(mBatteryController.isPluggedInWireless()).thenReturn(false); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN wakeup @@ -249,22 +244,6 @@ public class DozeTriggersTest extends SysuiTestCase { // WHEN the pick up gesture is triggered and keyguard IS occluded when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mBatteryController.isPluggedInWireless()).thenReturn(false); - mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); - - // THEN never wakeup - verify(mMachine, never()).wakeUp(); - } - - @Test - public void testPickupGestureWirelessCharger() { - // GIVEN device is in doze (screen blank, but running doze sensors) - when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); - - // WHEN the pick up gesture is triggered - // and device IS on a wireless charger - when(mKeyguardStateController.isOccluded()).thenReturn(false); - when(mBatteryController.isPluggedInWireless()).thenReturn(true); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN never wakeup diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 5ff316e2efec..c532ed5ab651 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -34,8 +34,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AnimatedStateListDrawable; import android.hardware.biometrics.BiometricSourceType; -import android.hardware.biometrics.SensorLocationInternal; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Pair; @@ -77,9 +75,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; -import java.util.ArrayList; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -182,7 +177,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-attached - Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location + Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); @@ -197,7 +192,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { @Test public void testUpdatePaddingBasedOnResolutionScale() { // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 - Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location + Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location when(mAuthController.getScaleFactor()).thenReturn(5f); // WHEN lock icon view controller is initialized and attached @@ -215,20 +210,19 @@ public class LockIconViewControllerTest extends SysuiTestCase { // GIVEN fp sensor location is not available pre-init when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - when(mAuthController.getUdfpsProps()).thenReturn(null); mLockIconViewController.init(); captureAttachListener(); mAttachListener.onViewAttachedToWindow(mLockIconView); - // GIVEN fp sensor location is available post-atttached + // GIVEN fp sensor location is available post-attached captureAuthControllerCallback(); - Pair<Integer, PointF> udfps = setupUdfps(); + Pair<Float, PointF> udfps = setupUdfps(); // WHEN all authenticators are registered mAuthControllerCallback.onAllAuthenticatorsRegistered(); mDelayableExecutor.runAllReady(); - // THEN lock icon view location is updated with the same coordinates as fpProps + // THEN lock icon view location is updated with the same coordinates as auth controller vals verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), eq(PADDING)); } @@ -402,21 +396,10 @@ public class LockIconViewControllerTest extends SysuiTestCase { verify(mLockIconView).setTranslationX(0); } - private Pair<Integer, PointF> setupUdfps() { + private Pair<Float, PointF> setupUdfps() { when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); final PointF udfpsLocation = new PointF(50, 75); - final int radius = 33; - final FingerprintSensorPropertiesInternal fpProps = - new FingerprintSensorPropertiesInternal( - /* sensorId */ 0, - /* strength */ 0, - /* max enrollments per user */ 5, - /* component info */ new ArrayList<>(), - /* sensorType */ 3, - /* halControlsIllumination */ true, - /* resetLockoutRequiresHwToken */ false, - List.of(new SensorLocationInternal("" /* displayId */, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius))); + final float radius = 33f; when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsRadius()).thenReturn(radius); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 63a0de8578d7..1310d69d0d0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -21,7 +21,6 @@ import android.animation.AnimatorSet import android.app.PendingIntent import android.app.smartspace.SmartspaceAction import android.content.Context -import org.mockito.Mockito.`when` as whenever import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager @@ -67,8 +66,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.KotlinArgumentCaptor -import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.withArgCaptor @@ -92,6 +91,7 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever private const val KEY = "TEST_KEY" private const val PACKAGE = "PKG" @@ -339,6 +339,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.player).thenReturn(view) whenever(viewHolder.appIcon).thenReturn(appIcon) whenever(viewHolder.albumView).thenReturn(albumView) + whenever(albumView.foreground).thenReturn(mock(Drawable::class.java)) whenever(viewHolder.titleText).thenReturn(titleText) whenever(viewHolder.artistText).thenReturn(artistText) whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 6d3a5fecc826..ec20271ea43b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -336,7 +336,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { // WHEN the position algorithm is run positionClock(); // THEN the padding DOESN'T adjust for keyguard status height. - assertThat(mClockPositionAlgorithm.getMinStackScrollerPadding()) + assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()) .isEqualTo(mKeyguardStatusBarHeaderHeight); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 94e6b9a12a93..11f96cec6369 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -610,13 +610,13 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mLockIconViewController.getTop()).thenReturn(80f); when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5); - // Available space (100 - 10 - 15 = 75) + // Available space (100 - 0 - 15 = 85) when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100); - when(mNotificationStackScrollLayoutController.getTopPadding()).thenReturn(10); + when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); mNotificationPanelViewController.updateResources(); assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications()) - .isEqualTo(75); + .isEqualTo(85); } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 649328d1eef3..9b29bae1fc46 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -242,6 +242,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int getCurrentUserIdLocked(); + Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int windowId); + boolean isAccessibilityButtonShown(); /** @@ -551,8 +554,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -576,11 +577,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -628,8 +629,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float [] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -653,11 +652,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -706,8 +705,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -731,11 +728,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -786,8 +783,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -811,11 +806,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -865,8 +860,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -889,12 +882,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -1672,21 +1664,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mInvocationHandler.startInputLocked(connection, editorInfo, restarting); } - - @Nullable - Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) { - final WindowInfo windowInfo = - mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId); - if (windowInfo == null) { - Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = " - + resolvedWindowId); - return new Pair<>(null, null); - } - - final MagnificationSpec spec = new MagnificationSpec(); - spec.setTo(windowInfo.mMagnificationSpec); - return new Pair<>(windowInfo.mTransformMatrix, spec); + private Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int resolvedWindowId) { + return mSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4806514a0e13..99c849527034 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -68,6 +68,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -99,6 +100,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArraySet; import android.util.IntArray; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -458,6 +460,41 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int windowId) { + WindowInfo windowInfo; + synchronized (mLock) { + windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(windowId); + } + if (windowInfo != null) { + final MagnificationSpec spec = new MagnificationSpec(); + spec.setTo(windowInfo.mMagnificationSpec); + return new Pair<>(windowInfo.mTransformMatrix, spec); + } else { + // If the framework doesn't track windows, we fall back to get the pair of + // transformation matrix and MagnificationSpe from the WindowManagerService's + // WindowState. + IBinder token; + synchronized (mLock) { + token = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(mCurrentUserId, + windowId); + } + Pair<Matrix, MagnificationSpec> pair = + mWindowManagerService.getWindowTransformationMatrixAndMagnificationSpec(token); + final float[] outTransformationMatrix = new float[9]; + final Matrix tmpMatrix = pair.first; + final MagnificationSpec spec = pair.second; + if (!spec.isNop()) { + tmpMatrix.postScale(spec.scale, spec.scale); + tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); + } + tmpMatrix.getValues(outTransformationMatrix); + + return new Pair<>(outTransformationMatrix, pair.second); + } + } + + @Override public void onServiceInfoChangedLocked(AccessibilityUserState userState) { mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId, userState.mBoundServices); @@ -3750,12 +3787,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boundsInScreenBeforeMagnification.centerY()); // Invert magnification if needed. - final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked( - focus.getWindowId()); + final Pair<float[], MagnificationSpec> pair = + getWindowTransformationMatrixAndMagnificationSpec(focus.getWindowId()); MagnificationSpec spec = null; - if (windowInfo != null) { + if (pair != null && pair.second != null) { spec = new MagnificationSpec(); - spec.setTo(windowInfo.mMagnificationSpec); + spec.setTo(pair.second); } if (spec != null && !spec.isNop()) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 8e32a7a8e255..6d3620fa2ee6 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3225,10 +3225,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN); mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG); } + // Just show fill dialog once, so disabled after shown. + // Note: Cannot disable before requestShowFillDialog() because the method + // need to check whether fill dialog enabled. + setFillDialogDisabled(); return; } else { setFillDialogDisabled(); } + } if (response.supportsInlineSuggestions()) { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 62bb9f155c34..3b11038daf7e 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -732,9 +732,12 @@ public class CompanionDeviceManagerService extends SystemService { String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException { enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand"); - new CompanionDeviceShellCommand( - CompanionDeviceManagerService.this, mAssociationStore) - .exec(this, in, out, err, args, callback, resultReceiver); + + final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand( + CompanionDeviceManagerService.this, + mAssociationStore, + mDevicePresenceMonitor); + cmd.exec(this, in, out, err, args, callback, resultReceiver); } @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index fd130852a43a..6a19a42723c5 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -21,6 +21,8 @@ import android.os.ShellCommand; import android.util.Log; import android.util.Slog; +import com.android.server.companion.presence.CompanionDevicePresenceMonitor; + import java.io.PrintWriter; import java.util.List; @@ -29,20 +31,24 @@ class CompanionDeviceShellCommand extends ShellCommand { private final CompanionDeviceManagerService mService; private final AssociationStore mAssociationStore; + private final CompanionDevicePresenceMonitor mDevicePresenceMonitor; CompanionDeviceShellCommand(CompanionDeviceManagerService service, - AssociationStore associationStore) { + AssociationStore associationStore, + CompanionDevicePresenceMonitor devicePresenceMonitor) { mService = service; mAssociationStore = associationStore; + mDevicePresenceMonitor = devicePresenceMonitor; } @Override public int onCommand(String cmd) { final PrintWriter out = getOutPrintWriter(); + final int associationId; try { switch (cmd) { case "list": { - final int userId = getNextArgInt(); + final int userId = getNextIntArgRequired(); final List<AssociationInfo> associationsForUser = mAssociationStore.getAssociationsForUser(userId); for (AssociationInfo association : associationsForUser) { @@ -55,7 +61,7 @@ class CompanionDeviceShellCommand extends ShellCommand { break; case "associate": { - int userId = getNextArgInt(); + int userId = getNextIntArgRequired(); String packageName = getNextArgRequired(); String address = getNextArgRequired(); mService.legacyCreateAssociation(userId, address, packageName, null); @@ -63,7 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand { break; case "disassociate": { - final int userId = getNextArgInt(); + final int userId = getNextIntArgRequired(); final String packageName = getNextArgRequired(); final String address = getNextArgRequired(); final AssociationInfo association = @@ -80,6 +86,16 @@ class CompanionDeviceShellCommand extends ShellCommand { } break; + case "simulate-device-appeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceAppeared(associationId); + break; + + case "simulate-device-disappeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceDisappeared(associationId); + break; + default: return handleDefaultCommands(cmd); } @@ -91,10 +107,6 @@ class CompanionDeviceShellCommand extends ShellCommand { } } - private int getNextArgInt() { - return Integer.parseInt(getNextArgRequired()); - } - @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -108,7 +120,31 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS"); pw.println(" Remove an existing Association."); pw.println(" clear-association-memory-cache"); - pw.println(" Clear the in-memory association cache and reload all association " - + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); + pw.println(" Clear the in-memory association cache and reload all association "); + pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-appeared ASSOCIATION_ID"); + pw.println(" Make CDM act as if the given companion device has appeared."); + pw.println(" I.e. bind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); + pw.println(" The CDM will consider the devices as present for 60 seconds and then"); + pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'"); + pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out" + + "."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-disappeared ASSOCIATION_ID"); + pw.println(" Make CDM act as if the given companion device has disappeared."); + pw.println(" I.e. unbind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback."); + pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); + pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); + pw.println(" 60 seconds ago."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + } + + private int getNextIntArgRequired() { + return Integer.parseInt(getNextArgRequired()); } } diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 24be1b6fd701..37e83697c787 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -16,11 +16,19 @@ package com.android.server.companion.presence; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SHELL_UID; + import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.bluetooth.BluetoothAdapter; import android.companion.AssociationInfo; import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.util.Log; import com.android.server.companion.AssociationStore; @@ -72,6 +80,11 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>(); private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>(); + // Tracking "simulated" presence. Used for debugging and testing only. + private final @NonNull Set<Integer> mSimulated = new HashSet<>(); + private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper = + new SimulatedDevicePresenceSchedulerHelper(); + public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore, @NonNull Callback callback) { mAssociationStore = associationStore; @@ -106,7 +119,8 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange public boolean isDevicePresent(int associationId) { return mReportedSelfManagedDevices.contains(associationId) || mConnectedBtDevices.contains(associationId) - || mNearbyBleDevices.contains(associationId); + || mNearbyBleDevices.contains(associationId) + || mSimulated.contains(associationId); } /** @@ -155,6 +169,45 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble"); } + /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */ + @TestApi + public void simulateDeviceAppeared(int associationId) { + // IMPORTANT: this API should only be invoked via the + // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to + // make this call are SHELL and ROOT. + // No other caller (including SYSTEM!) should be allowed. + enforceCallerShellOrRoot(); + // Make sure the association exists. + enforceAssociationExists(associationId); + + onDevicePresent(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + + mSchedulerHelper.scheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId); + } + + /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */ + @TestApi + public void simulateDeviceDisappeared(int associationId) { + // IMPORTANT: this API should only be invoked via the + // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to + // make this call are SHELL and ROOT. + // No other caller (including SYSTEM!) should be allowed. + enforceCallerShellOrRoot(); + // Make sure the association exists. + enforceAssociationExists(associationId); + + mSchedulerHelper.unscheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId); + + onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + } + + private void enforceAssociationExists(int associationId) { + if (mAssociationStore.getAssociationById(associationId) == null) { + throw new IllegalArgumentException( + "Association with id " + associationId + " does not exist."); + } + } + private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource, int newDeviceAssociationId, @NonNull String sourceLoggingTag) { if (DEBUG) { @@ -212,6 +265,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange mConnectedBtDevices.remove(associationId); mNearbyBleDevices.remove(associationId); mReportedSelfManagedDevices.remove(associationId); + mSimulated.remove(associationId); } /** @@ -232,4 +286,36 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange // CompanionDeviceManagerService will know that the association is removed, and will do // what's needed. } + + private static void enforceCallerShellOrRoot() { + final int callingUid = Binder.getCallingUid(); + if (callingUid == SHELL_UID || callingUid == ROOT_UID) return; + + throw new SecurityException("Caller is neither Shell nor Root"); + } + + private class SimulatedDevicePresenceSchedulerHelper extends Handler { + SimulatedDevicePresenceSchedulerHelper() { + super(Looper.getMainLooper()); + } + + void scheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) { + // First, unschedule if it was scheduled previously. + if (hasMessages(/* what */ associationId)) { + removeMessages(/* what */ associationId); + } + + sendEmptyMessageDelayed(/* what */ associationId, 60 * 1000 /* 60 seconds */); + } + + void unscheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) { + removeMessages(/* what */ associationId); + } + + @Override + public void handleMessage(@NonNull Message msg) { + final int associationId = msg.what; + onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + } + } } diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java index 18fb6a480f29..08a6719a016a 100644 --- a/services/core/java/com/android/server/am/DropboxRateLimiter.java +++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java @@ -24,9 +24,14 @@ import com.android.internal.annotations.GuardedBy; /** Rate limiter for adding errors into dropbox. */ public class DropboxRateLimiter { - private static final long RATE_LIMIT_BUFFER_EXPIRY = 15 * DateUtils.SECOND_IN_MILLIS; - private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.SECOND_IN_MILLIS; - private static final int RATE_LIMIT_ALLOWED_ENTRIES = 5; + // After RATE_LIMIT_ALLOWED_ENTRIES have been collected (for a single breakdown of + // process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has + // elapsed, after which the current count for this breakdown will be reset. + private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS; + // The time duration after which the rate limit buffer will be cleared. + private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION; + // The number of entries to keep per breakdown of process/eventType. + private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6; @GuardedBy("mErrorClusterRecords") private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>(); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index fc73a5955001..cceacd8c6fa3 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2792,6 +2792,15 @@ public final class ProcessList { } int N = procs.size(); + for (int i = 0; i < N; ++i) { + final ProcessRecord proc = procs.get(i).first; + try { + Process.setProcessFrozen(proc.getPid(), proc.uid, true); + } catch (Exception e) { + Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName); + } + } + for (int i=0; i<N; i++) { final Pair<ProcessRecord, Boolean> proc = procs.get(i); removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second, diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 122a950e4fd3..d8aa9aa6591e 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -486,8 +486,7 @@ public class SpatializerHelper { for (SADeviceState deviceState : mSADevices) { if (deviceType == deviceState.mDeviceType - && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress)) - || !wireless) { + && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) { isInList = true; if (forceEnable) { deviceState.mEnabled = true; @@ -511,8 +510,7 @@ public class SpatializerHelper { for (SADeviceState deviceState : mSADevices) { if (deviceType == deviceState.mDeviceType - && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress)) - || !wireless) { + && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) { deviceState.mEnabled = false; break; } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 5ae3f337d0a5..e4e9d1d49a6e 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1712,6 +1712,13 @@ public class InputManagerService extends IInputManager.Stub mPointerIconDisplayContext = null; } + synchronized (mAdditionalDisplayInputPropertiesLock) { + setPointerIconVisible(AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE, + displayId); + setPointerAcceleration(AdditionalDisplayInputProperties.DEFAULT_POINTER_ACCELERATION, + displayId); + } + mNative.displayRemoved(displayId); } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index db17c1056e39..8180e66166d9 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -64,7 +64,9 @@ final class HandwritingEventReceiverSurface { | InputConfig.INTERCEPTS_STYLUS | InputConfig.TRUSTED_OVERLAY; - // The touchable region of this input surface is not initially configured. + // Configure the surface to receive stylus events across the entire display. + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER); @@ -81,10 +83,6 @@ final class HandwritingEventReceiverSurface { mWindowHandle.ownerUid = imeUid; mWindowHandle.inputConfig &= ~InputConfig.SPY; - // Update the touchable region so that the IME can intercept stylus events - // across the entire display. - mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); - new SurfaceControl.Transaction() .setInputWindowInfo(mInputSurface, mWindowHandle) .apply(); diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java index a70677222506..f89b6aedf1f5 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java @@ -91,7 +91,7 @@ final class HandwritingModeController { * InputEventReceiver that batches events according to the current thread's Choreographer. */ @UiThread - void initializeHandwritingSpy(int displayId, IBinder focusedWindowToken) { + void initializeHandwritingSpy(int displayId) { // When resetting, reuse resources if we are reinitializing on the same display. reset(displayId == mCurrentDisplayId); mCurrentDisplayId = displayId; @@ -115,12 +115,6 @@ final class HandwritingModeController { mHandwritingSurface = new HandwritingEventReceiverSurface( name, displayId, surface, channel); - // Configure the handwriting window to receive events over the focused window's bounds. - mWindowManagerInternal.replaceInputSurfaceTouchableRegionWithWindowCrop( - mHandwritingSurface.getSurface(), - mHandwritingSurface.getInputWindowHandle(), - focusedWindowToken); - // Use a dup of the input channel so that event processing can be paused by disposing the // event receiver without causing a fd hangup. mHandwritingEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver( @@ -149,7 +143,8 @@ final class HandwritingModeController { */ @UiThread @Nullable - HandwritingSession startHandwritingSession(int requestId, int imePid, int imeUid) { + HandwritingSession startHandwritingSession( + int requestId, int imePid, int imeUid, IBinder focusedWindowToken) { if (mHandwritingSurface == null) { Slog.e(TAG, "Cannot start handwriting session: Handwriting was not initialized."); return null; @@ -158,12 +153,20 @@ final class HandwritingModeController { Slog.e(TAG, "Cannot start handwriting session: Invalid request id: " + requestId); return null; } - if (!mRecordingGesture) { + if (!mRecordingGesture || mHandwritingBuffer.isEmpty()) { Slog.e(TAG, "Cannot start handwriting session: No stylus gesture is being recorded."); return null; } Objects.requireNonNull(mHandwritingEventReceiver, "Handwriting session was already transferred to IME."); + final MotionEvent downEvent = mHandwritingBuffer.get(0); + assert (downEvent.getActionMasked() == MotionEvent.ACTION_DOWN); + if (!mWindowManagerInternal.isPointInsideWindow( + focusedWindowToken, mCurrentDisplayId, downEvent.getRawX(), downEvent.getRawY())) { + Slog.e(TAG, "Cannot start handwriting session: " + + "Stylus gesture did not start inside the focused window."); + return null; + } if (DEBUG) Slog.d(TAG, "Starting handwriting session in display: " + mCurrentDisplayId); mInputManagerInternal.pilferPointers(mHandwritingSurface.getInputChannel().getToken()); @@ -226,13 +229,17 @@ final class HandwritingModeController { } if (!(ev instanceof MotionEvent)) { - Slog.e("Stylus", "Received non-motion event in stylus monitor."); + Slog.wtf(TAG, "Received non-motion event in stylus monitor."); return false; } final MotionEvent event = (MotionEvent) ev; if (!isStylusEvent(event)) { return false; } + if (event.getDisplayId() != mCurrentDisplayId) { + Slog.wtf(TAG, "Received stylus event associated with the incorrect display."); + return false; + } onStylusEvent(event); return true; diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index e62c5c13f04d..170331032086 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -30,6 +30,7 @@ import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethodSubtype; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodNavButtonFlags; @@ -148,10 +149,11 @@ final class IInputMethodInvoker { @AnyThread void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute, - boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) { + boolean restarting, @InputMethodNavButtonFlags int navButtonFlags, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { try { mTarget.startInput(startInputToken, inputContext, attribute, restarting, - navButtonFlags); + navButtonFlags, imeDispatcher); } catch (RemoteException e) { logRemoteException(e); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 6af00b3fbeea..ea2b1571c0ae 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -150,6 +150,7 @@ import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTr import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import android.window.ImeOnBackInvokedDispatcher; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -616,6 +617,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub IInputContext mCurInputContext; /** + * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to + * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME. + */ + ImeOnBackInvokedDispatcher mCurImeDispatcher; + + /** * The {@link IRemoteAccessibilityInputConnection} last provided by the current client. */ @Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection; @@ -2623,7 +2630,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final SessionState session = mCurClient.curSession; setEnabledSessionLocked(session); session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting, - navButtonFlags); + navButtonFlags, mCurImeDispatcher); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null, @@ -2733,7 +2740,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags, @StartInputReason int startInputReason, - int unverifiedTargetSdkVersion) { + int unverifiedTargetSdkVersion, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { // If no method is currently selected, do nothing. final String selectedMethodId = getSelectedMethodIdLocked(); if (selectedMethodId == null) { @@ -2777,6 +2785,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mCurClient = cs; mCurInputContext = inputContext; mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection; + mCurImeDispatcher = imeDispatcher; mCurVirtualDisplayToScreenMatrix = getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId, mDisplayIdToShowIme); @@ -3780,10 +3789,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, - int unverifiedTargetSdkVersion) { + int unverifiedTargetSdkVersion, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, startInputFlags, softInputMode, windowFlags, attribute, inputContext, - remoteAccessibilityInputConnection, unverifiedTargetSdkVersion); + remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, + imeDispatcher); } @NonNull @@ -3792,7 +3803,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext, @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, - int unverifiedTargetSdkVersion) { + int unverifiedTargetSdkVersion, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { if (windowToken == null) { Slog.e(TAG, "windowToken cannot be null."); return InputBindResult.NULL; @@ -3829,7 +3841,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client, windowToken, startInputFlags, softInputMode, windowFlags, attribute, inputContext, remoteAccessibilityInputConnection, - unverifiedTargetSdkVersion, userId); + unverifiedTargetSdkVersion, userId, imeDispatcher); } finally { Binder.restoreCallingIdentity(ident); } @@ -3857,7 +3869,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection, - int unverifiedTargetSdkVersion, @UserIdInt int userId) { + int unverifiedTargetSdkVersion, @UserIdInt int userId, + @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { if (DEBUG) { Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason=" + InputMethodDebug.startInputReasonToString(startInputReason) @@ -3868,7 +3881,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub + InputMethodDebug.startInputFlagsToString(startInputFlags) + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode) + " windowFlags=#" + Integer.toHexString(windowFlags) - + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion); + + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion + + " imeDispatcher=" + imeDispatcher); } final ClientState cs = mClients.get(client.asBinder()); @@ -3952,7 +3966,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (attribute != null) { return startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, attribute, startInputFlags, - startInputReason, unverifiedTargetSdkVersion); + startInputReason, unverifiedTargetSdkVersion, imeDispatcher); } return new InputBindResult( InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, @@ -3993,7 +4007,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (isTextEditor && attribute != null && shouldRestoreImeVisibility(windowToken, softInputMode)) { res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, - attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion); + attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion, + imeDispatcher); showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY); return res; @@ -4033,7 +4048,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, attribute, startInputFlags, - startInputReason, unverifiedTargetSdkVersion); + startInputReason, unverifiedTargetSdkVersion, + imeDispatcher); didStart = true; } showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, @@ -4065,7 +4081,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, attribute, startInputFlags, - startInputReason, unverifiedTargetSdkVersion); + startInputReason, unverifiedTargetSdkVersion, + imeDispatcher); didStart = true; } showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, @@ -4085,7 +4102,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (attribute != null) { res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, attribute, startInputFlags, - startInputReason, unverifiedTargetSdkVersion); + startInputReason, unverifiedTargetSdkVersion, + imeDispatcher); didStart = true; } showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, @@ -4115,7 +4133,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection, attribute, startInputFlags, - startInputReason, unverifiedTargetSdkVersion); + startInputReason, unverifiedTargetSdkVersion, + imeDispatcher); } else { res = InputBindResult.NULL_EDITOR_INFO; } @@ -5099,9 +5118,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_RESET_HANDWRITING: { synchronized (ImfLock.class) { if (mBindingController.supportsStylusHandwriting() - && getCurMethodLocked() != null && mCurFocusedWindow != null) { - mHwController.initializeHandwritingSpy( - mCurTokenDisplayId, mCurFocusedWindow); + && getCurMethodLocked() != null) { + mHwController.initializeHandwritingSpy(mCurTokenDisplayId); } else { mHwController.reset(); } @@ -5111,14 +5129,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_START_HANDWRITING: synchronized (ImfLock.class) { IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod == null) { + if (curMethod == null || mCurFocusedWindow == null) { return true; } final HandwritingModeController.HandwritingSession session = mHwController.startHandwritingSession( msg.arg1 /*requestId*/, msg.arg2 /*pid*/, - mBindingController.getCurMethodUid()); + mBindingController.getCurMethodUid(), + mCurFocusedWindow); if (session == null) { Slog.e(TAG, "Failed to start handwriting session for requestId: " + msg.arg1); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 92703ec86d06..604e8f3949f4 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -60,8 +60,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.NoSuchElementException; +import java.util.concurrent.CopyOnWriteArrayList; /** * This is the system implementation of a Session. Apps will interact with the @@ -1159,6 +1159,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void sendCommand(String packageName, int pid, int uid, String command, Bundle args, ResultReceiver cb) { try { + final String reason = TAG + ":" + command; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onCommand(packageName, pid, uid, command, args, cb); } catch (RemoteException e) { Log.e(TAG, "Remote failure in sendCommand.", e); @@ -1168,6 +1171,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void sendCustomAction(String packageName, int pid, int uid, String action, Bundle args) { try { + final String reason = TAG + ":custom-" + action; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onCustomAction(packageName, pid, uid, action, args); } catch (RemoteException e) { Log.e(TAG, "Remote failure in sendCustomAction.", e); @@ -1176,6 +1182,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void prepare(String packageName, int pid, int uid) { try { + final String reason = TAG + ":prepare"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPrepare(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in prepare.", e); @@ -1185,6 +1194,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId, Bundle extras) { try { + final String reason = TAG + ":prepareFromMediaId"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in prepareFromMediaId.", e); @@ -1194,6 +1206,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void prepareFromSearch(String packageName, int pid, int uid, String query, Bundle extras) { try { + final String reason = TAG + ":prepareFromSearch"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPrepareFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in prepareFromSearch.", e); @@ -1202,6 +1217,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { try { + final String reason = TAG + ":prepareFromUri"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPrepareFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in prepareFromUri.", e); @@ -1210,6 +1228,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void play(String packageName, int pid, int uid) { try { + final String reason = TAG + ":play"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPlay(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in play.", e); @@ -1219,6 +1240,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void playFromMediaId(String packageName, int pid, int uid, String mediaId, Bundle extras) { try { + final String reason = TAG + ":playFromMediaId"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in playFromMediaId.", e); @@ -1228,6 +1252,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void playFromSearch(String packageName, int pid, int uid, String query, Bundle extras) { try { + final String reason = TAG + ":playFromSearch"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPlayFromSearch(packageName, pid, uid, query, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in playFromSearch.", e); @@ -1236,6 +1263,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) { try { + final String reason = TAG + ":playFromUri"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPlayFromUri(packageName, pid, uid, uri, extras); } catch (RemoteException e) { Log.e(TAG, "Remote failure in playFromUri.", e); @@ -1244,6 +1274,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void skipToTrack(String packageName, int pid, int uid, long id) { try { + final String reason = TAG + ":skipToTrack"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onSkipToTrack(packageName, pid, uid, id); } catch (RemoteException e) { Log.e(TAG, "Remote failure in skipToTrack", e); @@ -1252,6 +1285,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void pause(String packageName, int pid, int uid) { try { + final String reason = TAG + ":pause"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPause(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in pause.", e); @@ -1260,6 +1296,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void stop(String packageName, int pid, int uid) { try { + final String reason = TAG + ":stop"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onStop(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in stop.", e); @@ -1268,6 +1307,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void next(String packageName, int pid, int uid) { try { + final String reason = TAG + ":next"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onNext(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in next.", e); @@ -1276,6 +1318,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void previous(String packageName, int pid, int uid) { try { + final String reason = TAG + ":previous"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onPrevious(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in previous.", e); @@ -1284,6 +1329,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void fastForward(String packageName, int pid, int uid) { try { + final String reason = TAG + ":fastForward"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onFastForward(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in fastForward.", e); @@ -1292,6 +1340,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void rewind(String packageName, int pid, int uid) { try { + final String reason = TAG + ":rewind"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onRewind(packageName, pid, uid); } catch (RemoteException e) { Log.e(TAG, "Remote failure in rewind.", e); @@ -1300,6 +1351,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void seekTo(String packageName, int pid, int uid, long pos) { try { + final String reason = TAG + ":seekTo"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onSeekTo(packageName, pid, uid, pos); } catch (RemoteException e) { Log.e(TAG, "Remote failure in seekTo.", e); @@ -1308,6 +1362,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void rate(String packageName, int pid, int uid, Rating rating) { try { + final String reason = TAG + ":rate"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onRate(packageName, pid, uid, rating); } catch (RemoteException e) { Log.e(TAG, "Remote failure in rate.", e); @@ -1316,6 +1373,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) { try { + final String reason = TAG + ":setPlaybackSpeed"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onSetPlaybackSpeed(packageName, pid, uid, speed); } catch (RemoteException e) { Log.e(TAG, "Remote failure in setPlaybackSpeed.", e); @@ -1325,6 +1385,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, int direction) { try { + final String reason = TAG + ":adjustVolume"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); if (asSystemService) { mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(), Process.SYSTEM_UID, direction); @@ -1338,6 +1401,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void setVolumeTo(String packageName, int pid, int uid, int value) { try { + final String reason = TAG + ":setVolumeTo"; + mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(), + pid, uid, packageName, reason); mCb.onSetVolumeTo(packageName, pid, uid, value); } catch (RemoteException e) { Log.e(TAG, "Remote failure in setVolumeTo.", e); diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java index 2824585bf5aa..39119948fa3e 100644 --- a/services/core/java/com/android/server/pm/ApkChecksums.java +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -34,13 +34,14 @@ import android.content.Context; import android.content.pm.ApkChecksum; import android.content.pm.Checksum; import android.content.pm.IOnChecksumsReadyListener; -import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningDetails.SignatureSchemeVersion; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; +import android.os.Environment; +import android.os.FileUtils; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; @@ -63,7 +64,6 @@ import android.util.apk.VerityBuilder; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.security.VerityUtils; -import com.android.server.LocalServices; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.ByteArrayOutputStream; @@ -637,9 +637,18 @@ public class ApkChecksums { return null; } + private static boolean containsFile(File dir, String filePath) { + if (dir == null) { + return false; + } + return FileUtils.contains(dir.getAbsolutePath(), filePath); + } + private static ApkChecksum extractHashFromFS(String split, String filePath) { // verity first - { + // Skip /product folder. + // TODO(b/231354111): remove this hack once we are allowed to change SELinux rules. + if (!containsFile(Environment.getProductDirectory(), filePath)) { byte[] hash = VerityUtils.getFsverityRootHash(filePath); if (hash != null) { return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 320b06f6dc3e..32f0f109821d 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -119,7 +119,7 @@ public class Installer extends SystemService { IInstalld.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES; private final boolean mIsolated; - + private volatile boolean mDeferSetFirstBoot; private volatile IInstalld mInstalld; private volatile Object mWarnIfHeld; @@ -171,6 +171,7 @@ public class Installer extends SystemService { mInstalld = IInstalld.Stub.asInterface(binder); try { invalidateMounts(); + executeDeferredActions(); } catch (InstallerException ignored) { } } else { @@ -180,6 +181,15 @@ public class Installer extends SystemService { } /** + * Perform any deferred actions on mInstalld while the connection could not be made. + */ + private void executeDeferredActions() throws InstallerException { + if (mDeferSetFirstBoot) { + setFirstBoot(); + } + } + + /** * Do several pre-flight checks before making a remote call. * * @return if the remote call should continue. @@ -291,8 +301,15 @@ public class Installer extends SystemService { return; } try { - mInstalld.setFirstBoot(); - } catch (RemoteException e) { + // mInstalld might be null if the connection could not be established. + if (mInstalld != null) { + mInstalld.setFirstBoot(); + } else { + // if it is null while trying to set the first boot, set a flag to try and set the + // first boot when the connection is eventually established + mDeferSetFirstBoot = true; + } + } catch (Exception e) { throw InstallerException.from(e); } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 4df54b74bb1d..fa0c6c393cdc 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -166,18 +166,19 @@ class ShortcutPackage extends ShortcutPackageItem { * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs. */ @GuardedBy("mLock") - final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>(); + private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>(); /** * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on * IDs. */ @GuardedBy("mLock") - private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0); + private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0); /** * All the share targets from the package */ + @GuardedBy("mLock") private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0); /** @@ -231,7 +232,9 @@ class ShortcutPackage extends ShortcutPackageItem { } public int getShortcutCount() { - return mShortcuts.size(); + synchronized (mLock) { + return mShortcuts.size(); + } } @Override @@ -272,7 +275,9 @@ class ShortcutPackage extends ShortcutPackageItem { @Nullable public ShortcutInfo findShortcutById(@Nullable final String id) { if (id == null) return null; - return mShortcuts.get(id); + synchronized (mLock) { + return mShortcuts.get(id); + } } public boolean isShortcutExistsAndInvisibleToPublisher(String id) { @@ -347,11 +352,14 @@ class ShortcutPackage extends ShortcutPackageItem { * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible. */ private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) { - final ShortcutInfo shortcut = mShortcuts.remove(id); - if (shortcut != null) { - removeIcon(shortcut); - shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED - | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL); + final ShortcutInfo shortcut; + synchronized (mLock) { + shortcut = mShortcuts.remove(id); + if (shortcut != null) { + removeIcon(shortcut); + shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED + | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL); + } } return shortcut; } @@ -524,14 +532,16 @@ class ShortcutPackage extends ShortcutPackageItem { public List<ShortcutInfo> deleteAllDynamicShortcuts() { final long now = mShortcutUser.mService.injectCurrentTimeMillis(); boolean changed = false; - for (int i = mShortcuts.size() - 1; i >= 0; i--) { - ShortcutInfo si = mShortcuts.valueAt(i); - if (si.isDynamic() && si.isVisibleToPublisher()) { - changed = true; - - si.setTimestamp(now); - si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); - si.setRank(0); // It may still be pinned, so clear the rank. + synchronized (mLock) { + for (int i = mShortcuts.size() - 1; i >= 0; i--) { + ShortcutInfo si = mShortcuts.valueAt(i); + if (si.isDynamic() && si.isVisibleToPublisher()) { + changed = true; + + si.setTimestamp(now); + si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); + si.setRank(0); // It may still be pinned, so clear the rank. + } } } removeAllShortcutsAsync(); @@ -874,59 +884,63 @@ class ShortcutPackage extends ShortcutPackageItem { */ public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets( @NonNull IntentFilter filter) { - final List<ShareTargetInfo> matchedTargets = new ArrayList<>(); - for (int i = 0; i < mShareTargets.size(); i++) { - final ShareTargetInfo target = mShareTargets.get(i); - for (ShareTargetInfo.TargetData data : target.mTargetData) { - if (filter.hasDataType(data.mMimeType)) { - // Matched at least with one data type - matchedTargets.add(target); - break; + synchronized (mLock) { + final List<ShareTargetInfo> matchedTargets = new ArrayList<>(); + for (int i = 0; i < mShareTargets.size(); i++) { + final ShareTargetInfo target = mShareTargets.get(i); + for (ShareTargetInfo.TargetData data : target.mTargetData) { + if (filter.hasDataType(data.mMimeType)) { + // Matched at least with one data type + matchedTargets.add(target); + break; + } } } - } - if (matchedTargets.isEmpty()) { - return new ArrayList<>(); - } + if (matchedTargets.isEmpty()) { + return new ArrayList<>(); + } - // Get the list of all dynamic shortcuts in this package. - final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); - // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are - // included in the result - findAll(shortcuts, ShortcutInfo::isNonManifestVisible, - ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION, - mShortcutUser.mService.mContext.getPackageName(), - 0, /*getPinnedByAnyLauncher=*/ false); + // Get the list of all dynamic shortcuts in this package. + final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are + // included in the result + findAll(shortcuts, ShortcutInfo::isNonManifestVisible, + ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION, + mShortcutUser.mService.mContext.getPackageName(), + 0, /*getPinnedByAnyLauncher=*/ false); - final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>(); - for (int i = 0; i < shortcuts.size(); i++) { - final Set<String> categories = shortcuts.get(i).getCategories(); - if (categories == null || categories.isEmpty()) { - continue; - } - for (int j = 0; j < matchedTargets.size(); j++) { - // Shortcut must have all of share target categories - boolean hasAllCategories = true; - final ShareTargetInfo target = matchedTargets.get(j); - for (int q = 0; q < target.mCategories.length; q++) { - if (!categories.contains(target.mCategories[q])) { - hasAllCategories = false; + final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>(); + for (int i = 0; i < shortcuts.size(); i++) { + final Set<String> categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } + for (int j = 0; j < matchedTargets.size(); j++) { + // Shortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = matchedTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!categories.contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i), + new ComponentName(getPackageName(), target.mTargetClass))); break; } } - if (hasAllCategories) { - result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i), - new ComponentName(getPackageName(), target.mTargetClass))); - break; - } } + return result; } - return result; } public boolean hasShareTargets() { - return !mShareTargets.isEmpty(); + synchronized (mLock) { + return !mShareTargets.isEmpty(); + } } /** @@ -935,38 +949,40 @@ class ShortcutPackage extends ShortcutPackageItem { * the app's Xml resource. */ int getSharingShortcutCount() { - if (mShareTargets.isEmpty()) { - return 0; - } + synchronized (mLock) { + if (mShareTargets.isEmpty()) { + return 0; + } - // Get the list of all dynamic shortcuts in this package - final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); - findAll(shortcuts, ShortcutInfo::isNonManifestVisible, - ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + // Get the list of all dynamic shortcuts in this package + final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); + findAll(shortcuts, ShortcutInfo::isNonManifestVisible, + ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); - int sharingShortcutCount = 0; - for (int i = 0; i < shortcuts.size(); i++) { - final Set<String> categories = shortcuts.get(i).getCategories(); - if (categories == null || categories.isEmpty()) { - continue; - } - for (int j = 0; j < mShareTargets.size(); j++) { - // A SharingShortcut must have all of share target categories - boolean hasAllCategories = true; - final ShareTargetInfo target = mShareTargets.get(j); - for (int q = 0; q < target.mCategories.length; q++) { - if (!categories.contains(target.mCategories[q])) { - hasAllCategories = false; + int sharingShortcutCount = 0; + for (int i = 0; i < shortcuts.size(); i++) { + final Set<String> categories = shortcuts.get(i).getCategories(); + if (categories == null || categories.isEmpty()) { + continue; + } + for (int j = 0; j < mShareTargets.size(); j++) { + // A SharingShortcut must have all of share target categories + boolean hasAllCategories = true; + final ShareTargetInfo target = mShareTargets.get(j); + for (int q = 0; q < target.mCategories.length; q++) { + if (!categories.contains(target.mCategories[q])) { + hasAllCategories = false; + break; + } + } + if (hasAllCategories) { + sharingShortcutCount++; break; } } - if (hasAllCategories) { - sharingShortcutCount++; - break; - } } + return sharingShortcutCount; } - return sharingShortcutCount; } /** @@ -1090,19 +1106,25 @@ class ShortcutPackage extends ShortcutPackageItem { // Now prepare to publish manifest shortcuts. List<ShortcutInfo> newManifestShortcutList = null; - try { - newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService, - getPackageName(), getPackageUserId(), mShareTargets); - } catch (IOException|XmlPullParserException e) { - Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e); + final int shareTargetSize; + synchronized (mLock) { + try { + shareTargetSize = mShareTargets.size(); + newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService, + getPackageName(), getPackageUserId(), mShareTargets); + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e); + } } final int manifestShortcutSize = newManifestShortcutList == null ? 0 : newManifestShortcutList.size(); if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) { Slog.d(TAG, - String.format("Package %s has %d manifest shortcut(s), and %d share target(s)", - getPackageName(), manifestShortcutSize, mShareTargets.size())); + String.format( + "Package %s has %d manifest shortcut(s), and %d share target(s)", + getPackageName(), manifestShortcutSize, shareTargetSize)); } + if (isNewApp && (manifestShortcutSize == 0)) { // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do. @@ -1701,37 +1723,38 @@ class ShortcutPackage extends ShortcutPackageItem { @Override public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { - final int size = mShortcuts.size(); - final int shareTargetSize = mShareTargets.size(); + synchronized (mLock) { + final int size = mShortcuts.size(); + final int shareTargetSize = mShareTargets.size(); - if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) { - return; // nothing to write. - } + if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) { + return; // nothing to write. + } - out.startTag(null, TAG_ROOT); + out.startTag(null, TAG_ROOT); - ShortcutService.writeAttr(out, ATTR_NAME, getPackageName()); - ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); - ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); - if (!forBackup) { - synchronized (mLock) { - ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate) + ShortcutService.writeAttr(out, ATTR_NAME, getPackageName()); + ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); + ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); + if (!forBackup) { + ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate ? AppSearchShortcutInfo.SCHEMA_VERSION : 0); } - } - getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup); + getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup); - for (int j = 0; j < size; j++) { - saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed()); - } + for (int j = 0; j < size; j++) { + saveShortcut( + out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed()); + } - if (!forBackup) { - for (int j = 0; j < shareTargetSize; j++) { - mShareTargets.get(j).saveToXml(out); + if (!forBackup) { + for (int j = 0; j < shareTargetSize; j++) { + mShareTargets.get(j).saveToXml(out); + } } - } - out.endTag(null, TAG_ROOT); + out.endTag(null, TAG_ROOT); + } } private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup, @@ -1917,38 +1940,38 @@ class ShortcutPackage extends ShortcutPackageItem { synchronized (ret.mLock) { ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute( parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION; - } - ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); - ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); - - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type != XmlPullParser.START_TAG) { - continue; - } - final int depth = parser.getDepth(); - final String tag = parser.getName(); - if (depth == outerDepth + 1) { - switch (tag) { - case ShortcutPackageInfo.TAG_ROOT: - ret.getPackageInfo().loadFromXml(parser, fromBackup); + ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); + ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); - continue; - case TAG_SHORTCUT: - final ShortcutInfo si = parseShortcut(parser, packageName, - shortcutUser.getUserId(), fromBackup); - // Don't use addShortcut(), we don't need to save the icon. - ret.mShortcuts.put(si.getId(), si); - continue; - case TAG_SHARE_TARGET: - ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); - continue; + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type != XmlPullParser.START_TAG) { + continue; } + final int depth = parser.getDepth(); + final String tag = parser.getName(); + if (depth == outerDepth + 1) { + switch (tag) { + case ShortcutPackageInfo.TAG_ROOT: + ret.getPackageInfo().loadFromXml(parser, fromBackup); + + continue; + case TAG_SHORTCUT: + final ShortcutInfo si = parseShortcut(parser, packageName, + shortcutUser.getUserId(), fromBackup); + // Don't use addShortcut(), we don't need to save the icon. + ret.mShortcuts.put(si.getId(), si); + continue; + case TAG_SHARE_TARGET: + ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); + continue; + } + } + ShortcutService.warnForInvalidTag(depth, tag); } - ShortcutService.warnForInvalidTag(depth, tag); } return ret; } @@ -2152,7 +2175,9 @@ class ShortcutPackage extends ShortcutPackageItem { @VisibleForTesting List<ShareTargetInfo> getAllShareTargetsForTest() { - return new ArrayList<>(mShareTargets); + synchronized (mLock) { + return new ArrayList<>(mShareTargets); + } } @Override @@ -2291,15 +2316,19 @@ class ShortcutPackage extends ShortcutPackageItem { private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) { Objects.requireNonNull(shortcuts); - for (ShortcutInfo si : shortcuts) { - mShortcuts.put(si.getId(), si); + synchronized (mLock) { + for (ShortcutInfo si : shortcuts) { + mShortcuts.put(si.getId(), si); + } } } @Nullable List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) { - return ids.stream().map(mShortcuts::get) - .filter(Objects::nonNull).collect(Collectors.toList()); + synchronized (mLock) { + return ids.stream().map(mShortcuts::get) + .filter(Objects::nonNull).collect(Collectors.toList()); + } } private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) { @@ -2318,10 +2347,12 @@ class ShortcutPackage extends ShortcutPackageItem { private void forEachShortcutStopWhen( @NonNull final Function<ShortcutInfo, Boolean> cb) { - for (int i = mShortcuts.size() - 1; i >= 0; i--) { - final ShortcutInfo si = mShortcuts.valueAt(i); - if (cb.apply(si)) { - return; + synchronized (mLock) { + for (int i = mShortcuts.size() - 1; i >= 0; i--) { + final ShortcutInfo si = mShortcuts.valueAt(i); + if (cb.apply(si)) { + return; + } } } } @@ -2461,6 +2492,7 @@ class ShortcutPackage extends ShortcutPackageItem { }))); } + @GuardedBy("mLock") @Override void scheduleSaveToAppSearchLocked() { final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b14214189833..d8e7fbe8b296 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -27,7 +27,6 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; @@ -130,7 +129,6 @@ import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IAudioService; import android.media.session.MediaSessionLegacyHelper; -import android.os.BatteryManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.DeviceIdleManager; @@ -396,7 +394,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; - BatteryManagerInternal mBatteryManagerInternal; AudioManagerInternal mAudioManagerInternal; DisplayManager mDisplayManager; DisplayManagerInternal mDisplayManagerInternal; @@ -791,8 +788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onWakeUp() { synchronized (mLock) { - if (shouldEnableWakeGestureLp() - && getBatteryManagerInternal().getPlugType() != BATTERY_PLUGGED_WIRELESS) { + if (shouldEnableWakeGestureLp()) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, @@ -849,15 +845,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - BatteryManagerInternal getBatteryManagerInternal() { - synchronized (mServiceAcquireLock) { - if (mBatteryManagerInternal == null) { - mBatteryManagerInternal = - LocalServices.getService(BatteryManagerInternal.class); - } - return mBatteryManagerInternal; - } - } // returns true if the key was handled and should not be passed to the user private boolean backKeyPress() { diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java index 6059f9675e34..75e39ebb32d8 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayList.java +++ b/services/core/java/com/android/server/utils/WatchedArrayList.java @@ -416,7 +416,7 @@ public class WatchedArrayList<E> extends WatchableImpl dst.mStorage.ensureCapacity(end); for (int i = 0; i < end; i++) { final E val = Snapshots.maybeSnapshot(src.get(i)); - dst.add(i, val); + dst.add(val); } dst.seal(); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 63619e543a6d..f1dbad61a76d 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -446,6 +446,43 @@ final class AccessibilityController { // Not relevant for the window observer. } + public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + IBinder token) { + synchronized (mService.mGlobalLock) { + final Matrix transformationMatrix = new Matrix(); + final MagnificationSpec magnificationSpec = new MagnificationSpec(); + + final WindowState windowState = mService.mWindowMap.get(token); + if (windowState != null) { + windowState.getTransformationMatrix(new float[9], transformationMatrix); + + if (hasCallbacks()) { + final MagnificationSpec otherMagnificationSpec = + getMagnificationSpecForWindow(windowState); + if (otherMagnificationSpec != null && !otherMagnificationSpec.isNop()) { + magnificationSpec.setTo(otherMagnificationSpec); + } + } + } + + return new Pair<>(transformationMatrix, magnificationSpec); + } + } + + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow", + FLAGS_MAGNIFICATION_CALLBACK, + "windowState={" + windowState + "}"); + } + final int displayId = windowState.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + return displayMagnifier.getMagnificationSpecForWindow(windowState); + } + return null; + } + boolean hasCallbacks() { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index cb6559715c55..701fc9441acb 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -68,7 +68,6 @@ import static com.android.server.wm.AppTransition.isNormalTransit; import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp; import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -1143,17 +1142,13 @@ public class AppTransitionController { if (activity == null) { continue; } - if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) { - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "Delaying app transition for recents animation to finish"); - return false; - } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Check opening app=%s: allDrawn=%b startingDisplayed=%b " + "startingMoved=%b isRelaunching()=%b startingWindow=%s", activity, activity.allDrawn, activity.startingDisplayed, activity.startingMoved, activity.isRelaunching(), activity.mStartingWindow); + final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { return false; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index ee7d9a9b3f2d..46ce43335f01 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -153,10 +153,10 @@ class BLASTSyncEngine { for (WindowContainer wc : mRootMembers) { wc.waitForSyncTransactionCommit(wcAwaitingCommit); } - final Runnable callback = new Runnable() { + class CommitCallback implements Runnable { // Can run a second time if the action completes after the timeout. boolean ran = false; - public void run() { + public void onCommitted() { synchronized (mWm.mGlobalLock) { if (ran) { return; @@ -171,8 +171,23 @@ class BLASTSyncEngine { wcAwaitingCommit.clear(); } } + + // Called in timeout + @Override + public void run() { + // Sometimes we get a trace, sometimes we get a bugreport without + // a trace. Since these kind of ANRs can trigger such an issue, + // try and ensure we will have some visibility in both cases. + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout"); + Slog.e(TAG, "WM sent Transaction to organized, but never received" + + " commit callback. Application ANR likely to follow."); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + onCommitted(); + + } }; - merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::run); + CommitCallback callback = new CommitCallback(); + merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted); mWm.mH.postDelayed(callback, BLAST_TIMEOUT_DURATION); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady"); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index aaae82d07976..247117e707c8 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -187,7 +187,6 @@ class BackNavigationController { if (backType == BackNavigationInfo.TYPE_CALLBACK || currentActivity == null || currentTask == null - || currentTask.getDisplayContent().getImeContainer().isVisible() || currentActivity.isActivityTypeHome()) { return infoBuilder .setType(backType) diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a480c37fbcf3..2f00bc821678 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1584,14 +1584,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return true; } - void forAllWindowContainers(Consumer<WindowContainer> callback) { - callback.accept(this); - final int count = mChildren.size(); - for (int i = 0; i < count; i++) { - mChildren.get(i).forAllWindowContainers(callback); - } - } - /** * For all windows at or below this container call the callback. * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index f8bc26a7d91d..c0d7d1362ac3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -23,17 +23,18 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; import android.content.Context; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; import android.os.Bundle; import android.os.IBinder; +import android.util.Pair; import android.view.Display; import android.view.IInputFilter; import android.view.IRemoteAnimationFinishedCallback; import android.view.IWindow; import android.view.InputChannel; -import android.view.InputWindowHandle; import android.view.MagnificationSpec; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -459,6 +460,17 @@ public abstract class WindowManagerInternal { public abstract void getWindowFrame(IBinder token, Rect outBounds); /** + * Get the transformation matrix and MagnificationSpec given its token. + * + * @param token The token. + * @return The pair of the transformation matrix and magnification spec. + */ + // TODO (b/231663133): Long term solution for tracking window when the + // FLAG_RETRIEVE_INTERACTIVE_WINDOWS is unset. + public abstract Pair<Matrix, MagnificationSpec> + getWindowTransformationMatrixAndMagnificationSpec(IBinder token); + + /** * Opens the global actions dialog. */ public abstract void showGlobalActions(); @@ -849,24 +861,12 @@ public abstract class WindowManagerInternal { public abstract SurfaceControl getHandwritingSurfaceForDisplay(int displayId); /** - * Replaces the touchable region of the provided input surface with the crop of the window with - * the provided token. This method will associate the inputSurface with a copy of - * the given inputWindowHandle, where the copy is configured using - * {@link InputWindowHandle#replaceTouchableRegionWithCrop(SurfaceControl)} with the surface - * of the provided windowToken. - * - * This is a no-op if windowToken is not valid or the window is not found. - * - * This does not change any other properties of the inputSurface. - * - * This method exists to avoid leaking the window's SurfaceControl outside WindowManagerService. + * Returns {@code true} if the given point is within the window bounds of the given window. * - * @param inputSurface The surface for which the touchable region should be set. - * @param inputWindowHandle The {@link InputWindowHandle} for the input surface. - * @param windowToken The window whose bounds should be used as the touchable region for the - * inputSurface. + * @param windowToken the window whose bounds should be used for the hit test. + * @param displayX the x coordinate of the test point in the display's coordinate space. + * @param displayY the y coordinate of the test point in the display's coordinate space. */ - public abstract void replaceInputSurfaceTouchableRegionWithWindowCrop( - @NonNull SurfaceControl inputSurface, @NonNull InputWindowHandle inputWindowHandle, - @NonNull IBinder windowToken); + public abstract boolean isPointInsideWindow( + @NonNull IBinder windowToken, int displayId, float displayX, float displayY); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3906a19cee3a..8f1f7ece897b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -118,7 +118,6 @@ 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.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; @@ -173,6 +172,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -224,6 +224,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.MergedConfiguration; +import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.TimeUtils; @@ -3051,22 +3052,13 @@ public class WindowManagerService extends IWindowManager.Stub } } - void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { if (mRecentsAnimationController != null) { final RecentsAnimationController controller = mRecentsAnimationController; mRecentsAnimationController = null; controller.cleanupAnimation(reorderMode); - // TODO(multi-display): currently only default display support recents animation. - // Cancel any existing app transition animation running in the legacy transition - // framework. - final DisplayContent dc = getDefaultDisplayContentLocked(); - dc.mAppTransition.freeze(); - dc.forAllWindowContainers((wc) -> { - if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) { - wc.cancelAnimation(); - } - }); + // TODO(mult-display): currently only default display support recents animation. + getDefaultDisplayContentLocked().mAppTransition.updateBooster(); } } @@ -7769,6 +7761,13 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + IBinder token) { + return mAccessibilityController + .getWindowTransformationMatrixAndMagnificationSpec(token); + } + + @Override public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) { final WindowContainer container = displayId == INVALID_DISPLAY ? mRoot : mRoot.getDisplayContent(displayId); @@ -8241,23 +8240,15 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void replaceInputSurfaceTouchableRegionWithWindowCrop( - @NonNull SurfaceControl inputSurface, - @NonNull InputWindowHandle inputWindowHandle, - @NonNull IBinder windowToken) { + public boolean isPointInsideWindow(@NonNull IBinder windowToken, int displayId, + float displayX, float displayY) { synchronized (mGlobalLock) { final WindowState w = mWindowMap.get(windowToken); - if (w == null) { - return; + if (w == null || w.getDisplayId() != displayId) { + return false; } - // Make a copy of the InputWindowHandle to avoid leaking the window's - // SurfaceControl. - final InputWindowHandle localHandle = new InputWindowHandle(inputWindowHandle); - localHandle.replaceTouchableRegionWithCrop(w.getSurfaceControl()); - final SurfaceControl.Transaction t = mTransactionFactory.get(); - t.setInputWindowInfo(inputSurface, localHandle); - t.apply(); - t.close(); + + return w.getBounds().contains((int) displayX, (int) displayY); } } } @@ -8877,16 +8868,18 @@ public class WindowManagerService extends IWindowManager.Stub WindowState newFocusTarget = displayContent == null ? null : displayContent.findFocusedWindow(); if (newFocusTarget == null) { - ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for " - + "win=%s dropped since no candidate was found", + t.setFocusedWindow(null, null, displayId).apply(); + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s" + + " dropped focus so setting focus to null since no candidate" + + " was found", embeddedWindow); return; } - t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(), - inputToken, embeddedWindow.toString(), + t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(), displayId).apply(); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, - "Transfer focus request " + newFocusTarget, + "Focus request " + newFocusTarget, "reason=grantEmbeddedWindowFocus(false)"); } ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 8a7134e24cf8..7f466f38e96a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8513,6 +8513,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private boolean isDeviceOwnerUserId(int userId) { + synchronized (getLockObject()) { + return mOwners.getDeviceOwnerComponent() != null + && mOwners.getDeviceOwnerUserId() == userId; + } + } + private boolean isDeviceOwnerPackage(String packageName, int userId) { synchronized (getLockObject()) { return mOwners.hasDeviceOwner() @@ -12117,10 +12124,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isProfileOwner(caller) + Preconditions.checkCallAuthorization((isProfileOwner(caller) + && isManagedProfile(caller.getUserId())) || isDefaultDeviceOwner(caller), - "Caller is not profile owner or device owner;" - + " only profile owner or device owner may control the preferential" + "Caller is not managed profile owner or device owner;" + + " only managed profile owner or device owner may control the preferential" + " network service"); synchronized (getLockObject()) { final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( @@ -12147,11 +12155,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isProfileOwner(caller) + Preconditions.checkCallAuthorization((isProfileOwner(caller) + && isManagedProfile(caller.getUserId())) || isDefaultDeviceOwner(caller), - "Caller is not profile owner or device owner;" - + " only profile owner or device owner may retrieve the preferential" - + " network service configurations"); + "Caller is not managed profile owner or device owner;" + + " only managed profile owner or device owner may retrieve the " + + "preferential network service configurations"); synchronized (getLockObject()) { final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( caller.getUserId()); @@ -18266,7 +18275,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void updateNetworkPreferenceForUser(int userId, List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) { - if (!isManagedProfile(userId)) { + if (!isManagedProfile(userId) && !isDeviceOwnerUserId(userId)) { return; } List<ProfileNetworkPreference> preferences = new ArrayList<>(); diff --git a/services/proguard.flags b/services/proguard.flags index 425da6c11177..bad02b47031c 100644 --- a/services/proguard.flags +++ b/services/proguard.flags @@ -33,6 +33,11 @@ public <init>(...); } +# Accessed from com.android.compos APEX +-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog { + public static void write(...); +} + # Binder interfaces -keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface -keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 08df4381ad62..19df5a2ddd8b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -89,8 +89,10 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; +import android.util.Pair; import android.view.Display; import android.view.KeyEvent; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; @@ -145,6 +147,8 @@ public class AbstractAccessibilityServiceConnectionTest { private static final int USER_ID = 1; private static final int USER_ID2 = 2; private static final int INTERACTION_ID = 199; + private static final Pair<float[], MagnificationSpec> FAKE_MATRIX_AND_MAG_SPEC = + new Pair<>(new float[9], new MagnificationSpec()); private static final int PID = Process.myPid(); private static final long TID = Process.myTid(); private static final int UID = Process.myUid(); @@ -188,6 +192,8 @@ public class AbstractAccessibilityServiceConnectionTest { .thenReturn(mMockFingerprintGestureDispatcher); when(mMockSystemSupport.getMagnificationProcessor()) .thenReturn(mMockMagnificationProcessor); + when(mMockSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(anyInt())) + .thenReturn(FAKE_MATRIX_AND_MAG_SPEC); PowerManager powerManager = new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler); diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java index d1390c68e130..e68a8a0f3af8 100644 --- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java +++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java @@ -49,10 +49,11 @@ public class DropboxRateLimiterTest { assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); // Different processes and tags should not get rate limited either. assertFalse(mRateLimiter.shouldRateLimit("tag", "process2").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag2", "process").shouldRateLimit()); - // The 6th entry of the same process should be rate limited. + // The 7th entry of the same process should be rate limited. assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); } @@ -64,12 +65,13 @@ public class DropboxRateLimiterTest { assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); - // The 6th entry of the same process should be rate limited. + assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); + // The 7th entry of the same process should be rate limited. assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); - // After 11 seconds there should be nothing left in the buffer and the same type of entry + // After 11 minutes there should be nothing left in the buffer and the same type of entry // should not get rate limited anymore. - mClock.setOffsetMillis(11000); + mClock.setOffsetMillis(11 * 60 * 1000); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); } @@ -86,13 +88,15 @@ public class DropboxRateLimiterTest { mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(0, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); + assertEquals(0, + mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(1, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(2, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); - // After 11 seconds the rate limiting buffer will be cleared and rate limiting will stop. - mClock.setOffsetMillis(11000); + // After 11 minutes the rate limiting buffer will be cleared and rate limiting will stop. + mClock.setOffsetMillis(11 * 60 * 1000); // The first call after rate limiting stops will still return the number of dropped events. assertEquals(2, diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt index cb97c9bf91a3..e78f0c77d6b3 100644 --- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt @@ -20,9 +20,11 @@ import android.content.Context import android.content.ContextWrapper import android.hardware.display.DisplayViewport import android.hardware.input.InputManagerInternal +import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.view.Display +import android.view.PointerIcon import androidx.test.InstrumentationRegistry import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -31,6 +33,7 @@ import org.junit.Rule import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.doAnswer @@ -38,6 +41,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.junit.MockitoJUnit import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -219,4 +223,23 @@ class InputManagerServiceTests { secondRequestLatch.await(100, TimeUnit.MILLISECONDS)) verify(native, times(2)).setPointerDisplayId(anyInt()) } -}
\ No newline at end of file + + @Test + fun onDisplayRemoved_resetAllAdditionalInputProperties() { + localService.setVirtualMousePointerDisplayId(10) + localService.setPointerIconVisible(false, 10) + verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) + localService.setPointerAcceleration(5f, 10) + verify(native).setPointerAcceleration(eq(5f)) + + service.onDisplayRemoved(10) + verify(native).displayRemoved(eq(10)) + verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) + verify(native).setPointerAcceleration( + eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat())) + + localService.setVirtualMousePointerDisplayId(10) + verify(native).setPointerDisplayId(eq(10)) + verifyNoMoreInteractions(native) + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index a227cd3c6f5c..035249e32d74 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -70,6 +70,7 @@ import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -516,6 +517,7 @@ public class RecoverableKeyStoreManagerTest { } } + @Ignore("Causing breakages so ignoring to resolve, b/231667368") @Test public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception { int uid = Binder.getCallingUid(); @@ -539,6 +541,7 @@ public class RecoverableKeyStoreManagerTest { testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2()); } + @Ignore("Causing breakages so ignoring to resolve, b/231667368") @Test public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception { int uid = Binder.getCallingUid(); diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 20486b3e396d..8167b44ee59d 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -454,14 +454,14 @@ public class SystemConfigTest { + " <library \n" + " name=\"foo\"\n" + " file=\"" + mFooJar + "\"\n" - + " on-bootclasspath-before=\"Q\"\n" + + " on-bootclasspath-before=\"A\"\n" + " on-bootclasspath-since=\"W\"\n" + " />\n\n" + " </permissions>"; parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo("Q"); + assertThat(entry.onBootclasspathBefore).isEqualTo("A"); assertThat(entry.onBootclasspathSince).isEqualTo("W"); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 35921585d56f..eb6395b46120 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -39,10 +39,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 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.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; @@ -50,9 +48,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import android.graphics.Rect; import android.os.Binder; @@ -380,7 +376,7 @@ public class AppTransitionTests extends WindowTestsBase { doReturn(false).when(dc).onDescendantOrientationChanged(any()); final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "exiting app"); - final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord; + final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord; // Wait until everything in animation handler get executed to prevent the exiting window // from being removed during WindowSurfacePlacer Traversal. waitUntilHandlersIdle(); @@ -409,41 +405,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - public void testDelayWhileRecents() { - final DisplayContent dc = createNewDisplay(Display.STATE_ON); - doReturn(false).when(dc).onDescendantOrientationChanged(any()); - final Task task = createTask(dc); - - // Simulate activity1 launches activity2. - final ActivityRecord activity1 = createActivityRecord(task); - activity1.setVisible(true); - activity1.mVisibleRequested = false; - activity1.allDrawn = true; - final ActivityRecord activity2 = createActivityRecord(task); - activity2.setVisible(false); - activity2.mVisibleRequested = true; - activity2.allDrawn = true; - - dc.mClosingApps.add(activity1); - dc.mOpeningApps.add(activity2); - dc.prepareAppTransition(TRANSIT_OPEN); - assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN)); - - // Wait until everything in animation handler get executed to prevent the exiting window - // from being removed during WindowSurfacePlacer Traversal. - waitUntilHandlersIdle(); - - // Start recents - doReturn(true).when(task) - .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS)); - - dc.mAppTransitionController.handleAppTransitionReady(); - - verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); - verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); - } - - @Test public void testGetAnimationStyleResId() { // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without // specifying window type. diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java index e3485deb9080..ec94f8a1829f 100644 --- a/telecomm/java/android/telecom/PhoneAccountHandle.java +++ b/telecomm/java/android/telecom/PhoneAccountHandle.java @@ -46,6 +46,14 @@ import java.util.Objects; * See {@link PhoneAccount}, {@link TelecomManager}. */ public final class PhoneAccountHandle implements Parcelable { + /** + * Expected component name of Telephony phone accounts; ONLY used to determine if we should log + * the phone account handle ID. + */ + private static final ComponentName TELEPHONY_COMPONENT_NAME = + new ComponentName("com.android.phone", + "com.android.services.telephony.TelephonyConnectionService"); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196) private final ComponentName mComponentName; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -136,14 +144,23 @@ public final class PhoneAccountHandle implements Parcelable { @Override public String toString() { - // Note: Log.pii called for mId as it can contain personally identifying phone account - // information such as SIP account IDs. - return new StringBuilder().append(mComponentName) - .append(", ") - .append(Log.pii(mId)) - .append(", ") - .append(mUserHandle) - .toString(); + StringBuilder sb = new StringBuilder() + .append(mComponentName) + .append(", "); + + if (TELEPHONY_COMPONENT_NAME.equals(mComponentName)) { + // Telephony phone account handles are now keyed by subscription id which is not + // sensitive. + sb.append(mId); + } else { + // Note: Log.pii called for mId as it can contain personally identifying phone account + // information such as SIP account IDs. + sb.append(Log.pii(mId)); + } + sb.append(", "); + sb.append(mUserHandle); + + return sb.toString(); } @Override diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e21301eb32af..70fe6b10ef20 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4980,6 +4980,25 @@ public class CarrierConfigManager { KEY_PREFIX + "use_sip_uri_for_presence_subscribe_bool"; /** + * Flag indicating whether or not to use TEL URI when setting the entity uri field and + * contact element of each tuple. + * + * When {@code true}, the device sets the entity uri field and contact element to be + * TEL URI. This is done by first searching for the first TEL URI provided in + * p-associated-uri header. If there are no TEL URIs in the p-associated-uri header, we will + * convert the first SIP URI provided in the header to a TEL URI. If there are no URIs in + * the p-associated-uri header, we will then fall back to using the SIM card to generate the + * TEL URI. + * If {@code false}, the first URI provided in the p-associated-uri header is used, + * independent of the URI scheme. If there are no URIs available from p-associated-uri + * header, we will try to generate a SIP URI or TEL URI from the information provided by the + * SIM card, depending on the information available. + * @hide + */ + public static final String KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL = + KEY_PREFIX + "use_tel_uri_for_pidf_xml"; + + /** * An integer key associated with the period of time in seconds the non-rcs capability * information of each contact is cached on the device. * <p> diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index fa6de1ad2864..ac1f376cf6b5 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -1673,4 +1673,9 @@ public final class DataFailCause { return UNKNOWN; } } + + /** @hide */ + public static boolean isFailCauseExisting(@DataFailureCause int failCause) { + return sFailCauseMap.containsKey(failCause); + } } diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index d4fa1dda8bdf..62e16a5b83de 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -37,8 +37,8 @@ java_test_host { "general-tests", "vts", ], - data_device_bins: [ - "block_device_writer", + target_required: [ + "block_device_writer_module", ], data: [ ":ApkVerityTestCertDer", diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp index e5d009dc10fd..fdfa41fd4ca9 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -24,7 +24,12 @@ package { } cc_test { - name: "block_device_writer", + // Depending on how the test runs, the executable may be uploaded to different location. + // Before the bug in the file pusher is fixed, workaround by making the name unique. + // See b/124718249#comment12. + name: "block_device_writer_module", + stem: "block_device_writer", + srcs: ["block_device_writer.cpp"], cflags: [ "-D_FILE_OFFSET_BITS=64", @@ -37,7 +42,22 @@ cc_test { "libbase", "libutils", ], - compile_multilib: "first", + // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when + // the uploader does not pick up the executable from correct output location. The following + // workaround allows the test to: + // * upload the 32-bit exectuable for both 32 and 64 bits devices to use + // * refer to the same executable name in Java + // * no need to force the Java test to be archiecture specific. + // + // See b/145573317 for details. + multilib: { + lib32: { + suffix: "", + }, + lib64: { + suffix: "64", // not really used + }, + }, auto_gen_config: false, test_suites: [ diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java index 730daf32f20d..5c2c15b22bb0 100644 --- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java +++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java @@ -32,7 +32,7 @@ import java.util.ArrayList; * <p>To use this class, please push block_device_writer binary to /data/local/tmp. * 1. In Android.bp, add: * <pre> - * data_device_bins: ["block_device_writer"], + * target_required: ["block_device_writer_module"], * </pre> * 2. In AndroidText.xml, add: * <pre> |