diff options
139 files changed, 4609 insertions, 2292 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java index 764a3a8b1ed9..4aaa9f449b4f 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java @@ -17,27 +17,48 @@ package com.android.server.tare; import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.PowerManager; +import android.os.SystemClock; import android.util.IndentingPrintWriter; +import android.util.Log; +import android.util.Slog; /** Modifier that makes things more expensive in adaptive and full battery saver are active. */ class PowerSaveModeModifier extends Modifier { + private static final String TAG = "TARE-" + PowerSaveModeModifier.class.getSimpleName(); + private static final boolean DEBUG = InternalResourceService.DEBUG + || Log.isLoggable(TAG, Log.DEBUG); + private final InternalResourceService mIrs; - private final PowerManager mPowerManager; + private final PowerSaveModeTracker mPowerSaveModeTracker; PowerSaveModeModifier(@NonNull InternalResourceService irs) { super(); mIrs = irs; - mPowerManager = irs.getContext().getSystemService(PowerManager.class); + mPowerSaveModeTracker = new PowerSaveModeTracker(); + } + + @Override + public void setup() { + mPowerSaveModeTracker.startTracking(mIrs.getContext()); + } + + @Override + public void tearDown() { + mPowerSaveModeTracker.stopTracking(mIrs.getContext()); } @Override long getModifiedCostToProduce(long ctp) { - if (mPowerManager.isPowerSaveMode()) { + if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { return (long) (1.5 * ctp); } // TODO: get adaptive power save mode - if (mPowerManager.isPowerSaveMode()) { + if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { return (long) (1.25 * ctp); } return ctp; @@ -46,6 +67,45 @@ class PowerSaveModeModifier extends Modifier { @Override void dump(IndentingPrintWriter pw) { pw.print("power save="); - pw.println(mPowerManager.isPowerSaveMode()); + pw.println(mPowerSaveModeTracker.mPowerSaveModeEnabled); + } + + // TODO: migrate to relying on PowerSaveState and ServiceType.TARE + private final class PowerSaveModeTracker extends BroadcastReceiver { + private final PowerManager mPowerManager; + private volatile boolean mPowerSaveModeEnabled; + + private PowerSaveModeTracker() { + mPowerManager = mIrs.getContext().getSystemService(PowerManager.class); + } + + public void startTracking(@NonNull Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + context.registerReceiver(this, filter); + + // Initialise tracker state. + mPowerSaveModeEnabled = mPowerManager.isPowerSaveMode(); + } + + public void stopTracking(@NonNull Context context) { + context.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { + final boolean enabled = mPowerManager.isPowerSaveMode(); + if (DEBUG) { + Slog.d(TAG, "Power save mode changed to " + enabled + + ", fired @ " + SystemClock.elapsedRealtime()); + } + if (mPowerSaveModeEnabled != enabled) { + mPowerSaveModeEnabled = enabled; + mIrs.onDeviceStateChanged(); + } + } + } } } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index b843dcaaf680..cae6cdcf8f1f 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -25,6 +25,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE; import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY; +import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION; import static android.app.usage.UsageStatsManager.REASON_SUB_MASK; import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT; @@ -1581,7 +1582,11 @@ public class AppStandbyController // Only user force can bypass the delay restriction. If the user forced the // app into the RESTRICTED bucket, then a toast confirming the action // shouldn't be surprising. - if (Build.IS_DEBUGGABLE) { + // Exclude REASON_SUB_FORCED_USER_FLAG_INTERACTION since the RESTRICTED bucket + // isn't directly visible in that flow. + if (Build.IS_DEBUGGABLE + && (reason & REASON_SUB_MASK) + != REASON_SUB_FORCED_USER_FLAG_INTERACTION) { Toast.makeText(mContext, // Since AppStandbyController sits low in the lock hierarchy, // make sure not to call out with the lock held. diff --git a/api/Android.bp b/api/Android.bp index bbe26b73c721..40472b7e2d69 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -93,7 +93,6 @@ metalava_cmd = "$(location metalava)" // Silence reflection warnings. See b/168689341 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " metalava_cmd += " --quiet --no-banner --format=v2 " -metalava_cmd += " --hide ChangedThrows " genrule { name: "current-api-xml", diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 9c6402a6e36e..738b9cf237c9 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -37,7 +37,6 @@ #include "idmap2/Idmap.h" #include "idmap2/LogInfo.h" -using android::Res_value; using ::testing::NotNull; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 5a1d808af06f..32b3d1326d92 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -29,8 +29,6 @@ #include "idmap2/LogInfo.h" #include "idmap2/ResourceMapping.h" -using android::Res_value; - using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { diff --git a/core/api/current.txt b/core/api/current.txt index 71980a759a3d..ab1f733686c7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -189,6 +189,7 @@ package android { field public static final String START_VIEW_APP_FEATURES = "android.permission.START_VIEW_APP_FEATURES"; field public static final String START_VIEW_PERMISSION_USAGE = "android.permission.START_VIEW_PERMISSION_USAGE"; field public static final String STATUS_BAR = "android.permission.STATUS_BAR"; + field public static final String SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE = "android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"; field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW"; field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; @@ -5559,11 +5560,11 @@ package android.app { public final class GameState implements android.os.Parcelable { ctor public GameState(boolean, int); - ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle); + ctor public GameState(boolean, int, int, int); method public int describeContents(); - method @Nullable public String getDescription(); - method @NonNull public android.os.Bundle getMetadata(); + method public int getLabel(); method public int getMode(); + method public int getQuality(); method public boolean isLoading(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR; @@ -5677,6 +5678,7 @@ package android.app { } public class KeyguardManager { + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method @Deprecated public android.content.Intent createConfirmDeviceCredentialIntent(CharSequence, CharSequence); method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult); method @Deprecated public boolean inKeyguardRestrictedInputMode(); @@ -5685,6 +5687,7 @@ package android.app { method public boolean isKeyguardLocked(); method public boolean isKeyguardSecure(); method @Deprecated public android.app.KeyguardManager.KeyguardLock newKeyguardLock(String); + method @RequiresPermission(android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull android.app.KeyguardManager.KeyguardLockedStateListener); method public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback); } @@ -5700,6 +5703,10 @@ package android.app { method @Deprecated @RequiresPermission(android.Manifest.permission.DISABLE_KEYGUARD) public void reenableKeyguard(); } + @java.lang.FunctionalInterface public static interface KeyguardManager.KeyguardLockedStateListener { + method public void onKeyguardLockedStateChanged(boolean); + } + @Deprecated public static interface KeyguardManager.OnKeyguardExitResult { method @Deprecated public void onKeyguardExitResult(boolean); } @@ -31791,7 +31798,7 @@ package android.os { method public android.os.PowerManager.WakeLock newWakeLock(int, String); method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String); method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener); - field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 + field @Deprecated public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; field public static final String ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED"; field public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED = "android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED"; @@ -53112,7 +53119,7 @@ package android.view.inputmethod { field public static final int RESULT_SHOWN = 2; // 0x2 field public static final int RESULT_UNCHANGED_HIDDEN = 1; // 0x1 field public static final int RESULT_UNCHANGED_SHOWN = 0; // 0x0 - field public static final int SHOW_FORCED = 2; // 0x2 + field @Deprecated public static final int SHOW_FORCED = 2; // 0x2 field public static final int SHOW_IMPLICIT = 1; // 0x1 } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 594f46b6694b..7ec239d681d0 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -413,6 +413,7 @@ package android.os.storage { public class StorageManager { method public long computeStorageCacheBytes(@NonNull java.io.File); + method @Nullable public String getCloudMediaProvider(); method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int); method public void setCloudMediaProvider(@Nullable String); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9757b2ff318f..e64392b3b0c2 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2347,6 +2347,8 @@ package android.service.dreams { public abstract class DreamOverlayService extends android.app.Service { ctor public DreamOverlayService(); + method @Nullable public final CharSequence getDreamLabel(); + method public final boolean isPreviewMode(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); method public final void requestExit(); @@ -3082,6 +3084,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method public boolean isInputMethodPickerShown(); + field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L } } diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java index 979dd34e8276..fe6e5543a662 100644 --- a/core/java/android/app/GameState.java +++ b/core/java/android/app/GameState.java @@ -18,8 +18,6 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -88,12 +86,11 @@ public final class GameState implements Parcelable { // One of the states listed above. private final @GameStateMode int mMode; - // This is a game specific description. For example can be level or scene name. - private final @Nullable String mDescription; + // A developer-supplied enum, e.g. to indicate level or scene. + private final int mLabel; - // This contains any other game specific parameters not covered by the fields above. It can be - // quality parameter data, settings, or game modes. - private final @NonNull Bundle mMetaData; + // The developer-supplied enum, e.g. to indicate the current quality level. + private final int mQuality; /** * Create a GameState with the specified loading status. @@ -101,29 +98,28 @@ public final class GameState implements Parcelable { * @param mode The game state mode of type @GameStateMode. */ public GameState(boolean isLoading, @GameStateMode int mode) { - this(isLoading, mode, null, new Bundle()); + this(isLoading, mode, -1, -1); } /** * Create a GameState with the given state variables. * @param isLoading Whether the game is in the loading state. - * @param mode The game state mode of type @GameStateMode. - * @param description An optional description of the state. - * @param metaData Optional metadata. + * @param mode The game state mode. + * @param label An optional developer-supplied enum e.g. for the current level. + * @param quality An optional developer-supplied enum, e.g. for the current quality level. */ - public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description, - @NonNull Bundle metaData) { + public GameState(boolean isLoading, @GameStateMode int mode, int label, int quality) { mIsLoading = isLoading; mMode = mode; - mDescription = description; - mMetaData = metaData; + mLabel = label; + mQuality = quality; } private GameState(Parcel in) { mIsLoading = in.readBoolean(); mMode = in.readInt(); - mDescription = in.readString(); - mMetaData = in.readBundle(); + mLabel = in.readInt(); + mQuality = in.readInt(); } /** @@ -141,17 +137,19 @@ public final class GameState implements Parcelable { } /** - * @return The state description, or null if one is not set. + * @return The developer-supplied enum, e.g. to indicate level or scene. The default value (if + * not supplied) is -1. */ - public @Nullable String getDescription() { - return mDescription; + public int getLabel() { + return mLabel; } /** - * @return metadata associated with the state. + * @return The developer-supplied enum, e.g. to indicate the current quality level. The default + * value (if not suplied) is -1. */ - public @NonNull Bundle getMetadata() { - return mMetaData; + public int getQuality() { + return mQuality; } @Override @@ -163,8 +161,8 @@ public final class GameState implements Parcelable { public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeBoolean(mIsLoading); parcel.writeInt(mMode); - parcel.writeString(mDescription); - parcel.writeBundle(mMetaData); + parcel.writeInt(mLabel); + parcel.writeInt(mQuality); } /** diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 7c48a5738e51..fe0edfea59c8 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -510,7 +510,6 @@ interface IActivityManager { void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag); @UnsupportedAppUsage int getPackageProcessState(in String packageName, in String callingPackage); - void updateDeviceOwner(in String packageName); // Start of N transactions // Start Binder transaction tracking for all applications. diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 99100003e5b3..87ba197d8052 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -52,6 +52,7 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.util.Preconditions; import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; @@ -183,6 +184,10 @@ public class KeyguardManager { }) @interface LockTypes {} + // TODO(b/220379118): register only one binder listener and keep a map of listener to executor. + private final ArrayMap<KeyguardLockedStateListener, IKeyguardLockedStateListener> + mKeyguardLockedStateListeners = new ArrayMap<>(); + /** * Get an intent to prompt the user to confirm credentials (pin, pattern, password or biometrics * if enrolled) for the current user of the device. The caller is expected to launch this @@ -534,7 +539,7 @@ public class KeyguardManager { /** * Return whether the keyguard is currently locked. * - * @return true if keyguard is locked. + * @return {@code true} if the keyguard is locked. */ public boolean isKeyguardLocked() { try { @@ -550,7 +555,7 @@ public class KeyguardManager { * * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states. * - * @return true if a PIN, pattern or password is set or a SIM card is locked. + * @return {@code true} if a PIN, pattern or password is set or a SIM card is locked. */ public boolean isKeyguardSecure() { try { @@ -565,7 +570,7 @@ public class KeyguardManager { * keyguard password emergency screen). When in such mode, certain keys, * such as the Home key and the right soft keys, don't work. * - * @return true if in keyguard restricted input mode. + * @return {@code true} if in keyguard restricted input mode. * @deprecated Use {@link #isKeyguardLocked()} instead. */ public boolean inKeyguardRestrictedInputMode() { @@ -576,7 +581,7 @@ public class KeyguardManager { * Returns whether the device is currently locked and requires a PIN, pattern or * password to unlock. * - * @return true if unlocking the device currently requires a PIN, pattern or + * @return {@code true} if unlocking the device currently requires a PIN, pattern or * password. */ public boolean isDeviceLocked() { @@ -603,7 +608,7 @@ public class KeyguardManager { * * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure. * - * @return true if a PIN, pattern or password was set. + * @return {@code true} if a PIN, pattern or password was set. */ public boolean isDeviceSecure() { return isDeviceSecure(mContext.getUserId()); @@ -762,7 +767,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the password is valid, false otherwise + * @return {@code true} if the password is valid, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -821,7 +826,7 @@ public class KeyguardManager { * as the output of String#getBytes * @param complexity - complexity level imposed by the requester * as defined in {@code DevicePolicyManager.PasswordComplexity} - * @return true if the lock is successfully set, false otherwise + * @return {@code true} if the lock is successfully set, false otherwise * @hide */ @RequiresPermission(Manifest.permission.SET_INITIAL_LOCK) @@ -903,8 +908,8 @@ public class KeyguardManager { /** * Remove a weak escrow token. * - * @return true if the given handle refers to a valid weak token previously returned from - * {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. + * @return {@code true} if the given handle refers to a valid weak token previously returned + * from {@link #addWeakEscrowToken}, whether it's active or not. return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -944,7 +949,7 @@ public class KeyguardManager { /** * Register the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is registered successfully, return false otherwise. + * @return {@code true} if the listener is registered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -982,7 +987,7 @@ public class KeyguardManager { /** * Unregister the given WeakEscrowTokenRemovedListener. * - * @return true if the listener is unregistered successfully, return false otherwise. + * @return {@code true} if the listener is unregistered successfully, return false otherwise. * @hide */ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) @@ -1076,4 +1081,61 @@ public class KeyguardManager { throw new IllegalArgumentException("Unknown lock type " + lockType); } } + + /** + * Listener for keyguard locked state changes. + */ + @FunctionalInterface + public interface KeyguardLockedStateListener { + /** + * Callback function that executes when the keyguard locked state changes. + */ + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); + } + + /** + * Registers a listener to execute when the keyguard visibility changes. + * + * @param listener The listener to add to receive keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + try { + final IKeyguardLockedStateListener innerListener = + new IKeyguardLockedStateListener.Stub() { + @Override + public void onKeyguardLockedStateChanged(boolean isKeyguardLocked) { + executor.execute( + () -> listener.onKeyguardLockedStateChanged(isKeyguardLocked)); + } + }; + mWM.addKeyguardLockedStateListener(innerListener); + mKeyguardLockedStateListeners.put(listener, innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregisters a listener that executes when the keyguard visibility changes. + */ + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) { + synchronized (mKeyguardLockedStateListeners) { + IKeyguardLockedStateListener innerListener = mKeyguardLockedStateListeners.get( + listener); + if (innerListener == null) { + return; + } + try { + mWM.removeKeyguardLockedStateListener(innerListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mKeyguardLockedStateListeners.remove(listener); + } + } } diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index bf4b51431c6f..eed92c12eb83 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -37,6 +37,8 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; +import java.io.IOException; + /** * The SoundTriggerModule provides APIs to control sound models and sound detection * on a given sound trigger hardware module. @@ -137,13 +139,39 @@ public class SoundTriggerModule { if (model instanceof SoundTrigger.GenericSoundModel) { SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel( (SoundTrigger.GenericSoundModel) model); - soundModelHandle[0] = mService.loadModel(aidlModel); + try { + soundModelHandle[0] = mService.loadModel(aidlModel); + } finally { + // TODO(b/219825762): We should be able to use the entire object in a + // try-with-resources + // clause, instead of having to explicitly close internal fields. + if (aidlModel.data != null) { + try { + aidlModel.data.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close file", e); + } + } + } return SoundTrigger.STATUS_OK; } if (model instanceof SoundTrigger.KeyphraseSoundModel) { PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel( (SoundTrigger.KeyphraseSoundModel) model); - soundModelHandle[0] = mService.loadPhraseModel(aidlModel); + try { + soundModelHandle[0] = mService.loadPhraseModel(aidlModel); + } finally { + // TODO(b/219825762): We should be able to use the entire object in a + // try-with-resources + // clause, instead of having to explicitly close internal fields. + if (aidlModel.common.data != null) { + try { + aidlModel.common.data.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close file", e); + } + } + } return SoundTrigger.STATUS_OK; } return SoundTrigger.STATUS_BAD_VALUE; diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 315eef78724e..e3be4d32a014 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -183,25 +183,29 @@ public final class PowerManager { /** * Wake lock flag: Turn the screen on when the wake lock is acquired. * <p> - * Normally wake locks don't actually wake the device, they just cause - * the screen to remain on once it's already on. Think of the video player - * application as the normal behavior. Notifications that pop up and want - * the device to be on are the exception; use this flag to be like them. + * Normally wake locks don't actually wake the device, they just cause the screen to remain on + * once it's already on. This flag will cause the device to wake up when the wake lock is + * acquired. * </p><p> * Android TV playback devices attempt to turn on the HDMI-connected TV via HDMI-CEC on any * wake-up, including wake-ups triggered by wake locks. * </p><p> * Cannot be used with {@link #PARTIAL_WAKE_LOCK}. * </p> + * + * @deprecated Most applications should use {@link android.R.attr#turnScreenOn} or + * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the previous + * foreground app from being resumed first when the screen turns on. Note that this flag may + * require a permission in the future. */ + @Deprecated public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000; /** * Wake lock flag: When this wake lock is released, poke the user activity timer * so the screen stays on for a little longer. * <p> - * Will not turn the screen on if it is not already on. - * See {@link #ACQUIRE_CAUSES_WAKEUP} if you want that. + * This will not turn the screen on if it is not already on. * </p><p> * Cannot be used with {@link #PARTIAL_WAKE_LOCK}. * </p> diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index cc9833997102..48e2827f19e1 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -3022,9 +3022,17 @@ public class StorageManager { } } - /** @hide */ - @TestApi + /** + * Returns the authority of the current cloud media provider that was set by the + * {@link android.service.storage.ExternalStorageService} holding the + * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission via + * {@link #setCloudMediaProvider(String)}. + * + * @hide + */ @Nullable + @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public String getCloudMediaProvider() { try { return mStorageManager.getCloudMediaProvider(); diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 163d6ed4b18b..bfc3b8b39385 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -36,6 +36,9 @@ public abstract class DreamOverlayService extends Service { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = false; private boolean mShowComplications; + private boolean mIsPreviewMode; + @Nullable + private CharSequence mDreamLabel; private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override @@ -56,6 +59,8 @@ public abstract class DreamOverlayService extends Service { public final IBinder onBind(@NonNull Intent intent) { mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, DreamService.DEFAULT_SHOW_COMPLICATIONS); + mIsPreviewMode = intent.getBooleanExtra(DreamService.EXTRA_IS_PREVIEW, false); + mDreamLabel = intent.getCharSequenceExtra(DreamService.EXTRA_DREAM_LABEL); return mDreamOverlay.asBinder(); } @@ -84,4 +89,19 @@ public abstract class DreamOverlayService extends Service { public final boolean shouldShowComplications() { return mShowComplications; } + + /** + * Returns whether the dream is running in preview mode. + */ + public final boolean isPreviewMode() { + return mIsPreviewMode; + } + + /** + * Returns the user-facing label of the currently running dream. + */ + @Nullable + public final CharSequence getDreamLabel() { + return mDreamLabel; + } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 345917220b6b..db622d39b785 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -216,6 +216,18 @@ public class DreamService extends Service implements Window.Callback { "android.service.dreams.SHOW_COMPLICATIONS"; /** + * Extra containing a boolean for whether we are showing this dream in preview mode. + * @hide + */ + public static final String EXTRA_IS_PREVIEW = "android.service.dreams.IS_PREVIEW"; + + /** + * The user-facing label of the current dream service. + * @hide + */ + public static final String EXTRA_DREAM_LABEL = "android.service.dreams.DREAM_LABEL"; + + /** * The default value for whether to show complications on the overlay. * @hide */ @@ -258,15 +270,19 @@ public class DreamService extends Service implements Window.Callback { } public void bind(Context context, @Nullable ComponentName overlayService, - ComponentName dreamService) { + ComponentName dreamService, boolean isPreviewMode) { if (overlayService == null) { return; } + final ServiceInfo serviceInfo = fetchServiceInfo(context, dreamService); + final Intent overlayIntent = new Intent(); overlayIntent.setComponent(overlayService); overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS, - fetchShouldShowComplications(context, dreamService)); + fetchShouldShowComplications(context, serviceInfo)); + overlayIntent.putExtra(EXTRA_DREAM_LABEL, fetchDreamLabel(context, serviceInfo)); + overlayIntent.putExtra(EXTRA_IS_PREVIEW, isPreviewMode); context.bindService(overlayIntent, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE); @@ -988,8 +1004,11 @@ public class DreamService extends Service implements Window.Callback { // Connect to the overlay service if present. if (!mWindowless) { - mOverlayConnection.bind(this, intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), - new ComponentName(this, getClass())); + mOverlayConnection.bind( + /* context= */ this, + intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT), + new ComponentName(this, getClass()), + intent.getBooleanExtra(EXTRA_IS_PREVIEW, /* defaultValue= */ false)); } return mDreamServiceWrapper; @@ -1111,7 +1130,10 @@ public class DreamService extends Service implements Window.Callback { * @hide */ @Nullable - public static DreamMetadata getDreamMetadata(Context context, ServiceInfo serviceInfo) { + public static DreamMetadata getDreamMetadata(Context context, + @Nullable ServiceInfo serviceInfo) { + if (serviceInfo == null) return null; + final PackageManager pm = context.getPackageManager(); final TypedArray rawMetadata = readMetadata(pm, serviceInfo); @@ -1350,22 +1372,33 @@ public class DreamService extends Service implements Window.Callback { * {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}. */ private static boolean fetchShouldShowComplications(Context context, - ComponentName componentName) { + @Nullable ServiceInfo serviceInfo) { + final DreamMetadata metadata = getDreamMetadata(context, serviceInfo); + if (metadata != null) { + return metadata.showComplications; + } + return DEFAULT_SHOW_COMPLICATIONS; + } + + @Nullable + private static CharSequence fetchDreamLabel(Context context, + @Nullable ServiceInfo serviceInfo) { + if (serviceInfo == null) return null; + final PackageManager pm = context.getPackageManager(); + return serviceInfo.loadLabel(pm); + } + + @Nullable + private static ServiceInfo fetchServiceInfo(Context context, ComponentName componentName) { final PackageManager pm = context.getPackageManager(); try { - final ServiceInfo si = pm.getServiceInfo(componentName, + return pm.getServiceInfo(componentName, PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA)); - final DreamMetadata metadata = getDreamMetadata(context, si); - - if (metadata != null) { - return metadata.showComplications; - } } catch (PackageManager.NameNotFoundException e) { if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString()); } - - return DEFAULT_SHOW_COMPLICATIONS; + return null; } @Override diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java index 8fe6f71b598d..adc9251d89be 100644 --- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java +++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java @@ -40,7 +40,7 @@ public class FloatingToolbarRoot extends LinearLayout { private final IBinder mTargetInputToken; private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener; - private Rect mContentRect; + private final Rect mContentRect = new Rect(); private int mLastDownX = -1; private int mLastDownY = -1; @@ -57,7 +57,7 @@ public class FloatingToolbarRoot extends LinearLayout { * Sets the Rect that shows the selection toolbar content. */ public void setContentRect(Rect contentRect) { - mContentRect = contentRect; + mContentRect.set(contentRect); } @Override diff --git a/core/java/android/util/DumpableContainer.java b/core/java/android/util/DumpableContainer.java index e7b2acd5b500..fef5acd42e4e 100644 --- a/core/java/android/util/DumpableContainer.java +++ b/core/java/android/util/DumpableContainer.java @@ -18,8 +18,7 @@ package android.util; import android.annotation.NonNull; /** - * Objects that contains a list of {@link Dumpable}, which will be dumped when the object itself - * is dumped. + * Represents a container that manages {@link Dumpable dumpables}. */ public interface DumpableContainer { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ce21086931da..53b842a0f3a2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -18,6 +18,7 @@ package android.view; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import android.app.IAssistDataReceiver; @@ -199,6 +200,9 @@ interface IWindowManager boolean isKeyguardSecure(int userId); void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message); + void addKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + void removeKeyguardLockedStateListener(in IKeyguardLockedStateListener listener); + // Requires INTERACT_ACROSS_USERS_FULL permission void setSwitchingUser(boolean switching); diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 2edfda5d065c..a13579d0acad 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -328,7 +328,8 @@ public class SurfaceControlViewHost { */ public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { - return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection, + return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), + mAccessibilityEmbeddedConnection, mWm.getFocusGrantToken(), mRemoteInterface); } else { return null; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 844403298cc9..f7539d53da17 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2721,6 +2721,10 @@ public final class ViewRootImpl implements ViewParent, // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler); + if (mApplyInsetsRequested && !(mWillMove || mWillResize)) { + dispatchApplyInsets(host); + } + boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { @@ -2785,18 +2789,6 @@ public final class ViewRootImpl implements ViewParent, } } - if (mApplyInsetsRequested && !(mWillMove || mWillResize)) { - dispatchApplyInsets(host); - if (mLayoutRequested) { - // Short-circuit catching a new layout request here, so - // we don't need to go through two layout passes when things - // change due to fitting system windows, which can happen a lot. - windowSizeMayChange |= measureHierarchy(host, lp, - mView.getContext().getResources(), - desiredWindowWidth, desiredWindowHeight); - } - } - if (layoutRequested) { // Clear this now, so that if anything requests a layout in the // rest of this function we will catch it and re-run a full diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 27174630bfa3..017b8aa3cf6c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -46,6 +46,8 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -356,6 +358,21 @@ public final class InputMethodManager { /** @hide */ public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; + /** + * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed. + * + * <p> + * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer + * affect the next focused application to keep showing IME, in case of unexpected IME visible + * when the next focused app isn't be the IME requester. </p> + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id. + @UnsupportedAppUsage final IInputMethodManager mService; final Looper mMainLooper; @@ -1646,7 +1663,14 @@ public final class InputMethodManager { * Flag for {@link #showSoftInput} to indicate that the user has forced * the input method open (such as by long-pressing menu) so it should * not be closed until they explicitly do so. + * + * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead + * to the soft input remaining visible even when the calling application is closed. The + * use of this flag can make the soft input remains visible globally. Starting in + * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the + * caller is currently focused. */ + @Deprecated public static final int SHOW_FORCED = 0x0002; /** diff --git a/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl new file mode 100644 index 000000000000..ee5021918879 --- /dev/null +++ b/core/java/com/android/internal/policy/IKeyguardLockedStateListener.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.internal.policy; + +oneway interface IKeyguardLockedStateListener { + void onKeyguardLockedStateChanged(boolean isKeyguardLocked); +}
\ No newline at end of file diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c33b7c921740..e8f7b9343746 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -382,6 +382,7 @@ message ActivityRecordProto { optional bool in_size_compat_mode = 32; optional float min_aspect_ratio = 33; optional bool provides_max_bounds = 34; + optional bool enable_recents_screenshot = 35; } /* represents WindowToken */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 80317b8f5305..58a3bb4818df 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4119,6 +4119,13 @@ <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" android:protectionLevel="internal|preinstalled" /> + <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state. + <p>Protection level: internal|role + <p>Intended for use by ROLE_ASSISTANT only. + --> + <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" + android:protectionLevel="internal|role"/> + <!-- Must be required by a {@link android.service.autofill.AutofillService}, to ensure that only the system can bind to it. <p>Protection level: signature diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml index 0db08fb751d8..88bf18cb8857 100644 --- a/core/res/res/values-television/config.xml +++ b/core/res/res/values-television/config.xml @@ -24,19 +24,6 @@ <!-- Flags enabling default window features. See Window.java --> <bool name="config_defaultWindowFeatureOptionsPanel">false</bool> - <!-- The percentage of the screen width to use for the default width or height of - picture-in-picture windows. Regardless of the percent set here, calculated size will never - be smaller than @dimen/default_minimal_size_pip_resizable_task. --> - <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item> - - <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. - These values are in DPs and will be converted to pixel sizes internally. --> - <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">24x24</string> - - <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x55</integer> - <!-- The maximum height of the expanded horizontal picture-in-picture window --> <item name="config_pictureInPictureExpandedHorizontalHeight" format="dimension" type="dimen">110dp</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 05894d55358e..5ac30de631c5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3704,26 +3704,6 @@ snapped to any position between the first target and the last target. --> <bool name="config_dockedStackDividerFreeSnapMode">false</bool> - <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. - These values are in DPs and will be converted to pixel sizes internally. --> - <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string> - - <!-- The percentage of the screen width to use for the default width or height of - picture-in-picture windows. Regardless of the percent set here, calculated size will never - be smaller than @dimen/default_minimal_size_pip_resizable_task. --> - <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> - - <!-- The default aspect ratio for picture-in-picture windows. --> - <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">1.777778</item> - - <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size - will be used instead of an adaptive size based loosely on area. --> - <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item> - - <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x55</integer> - <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any ratio smaller than this is considered too tall and thin to be usable. Currently, this is the inverse of the max landscape aspect ratio (1:2.39), but this is an extremely diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 744c3dab9510..032d0b954ef1 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -606,6 +606,9 @@ <!-- The padding ratio of the Accessibility icon foreground drawable --> <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item> + <!-- The minimum window size of the accessibility window magnifier --> + <dimen name="accessibility_window_magnifier_min_size">122dp</dimen> + <!-- Margin around the various security views --> <dimen name="keyguard_muliuser_selector_margin">8dp</dimen> @@ -714,16 +717,6 @@ <!-- The default minimal size of a resizable task, in both dimensions. --> <dimen name="default_minimal_size_resizable_task">220dp</dimen> - <!-- The default minimal size of a PiP task, in both dimensions. --> - <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen> - - <!-- - The overridable minimal size of a PiP task, in both dimensions. - Different from default_minimal_size_pip_resizable_task, this is to limit the dimension - when the pinned stack size is overridden by app via minWidth/minHeight. - --> - <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> - <!-- Height of a task when in minimized mode from the top when launcher is resizable. --> <dimen name="task_height_of_minimized_mode">80dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d4513d0c4a3d..60a3398c93e6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -408,11 +408,6 @@ <java-symbol type="array" name="config_localPrivateDisplayPorts" /> <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> <java-symbol type="bool" name="config_enableAppWidgetService" /> - <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" /> - <java-symbol type="dimen" name="config_pictureInPictureDefaultSizePercent" /> - <java-symbol type="dimen" name="config_pictureInPictureDefaultAspectRatio" /> - <java-symbol type="dimen" name="config_pictureInPictureAspectRatioLimitForMinSize" /> - <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" /> @@ -1295,6 +1290,7 @@ <java-symbol type="array" name="vendor_required_apps_managed_user" /> <java-symbol type="array" name="vendor_required_apps_managed_profile" /> <java-symbol type="array" name="vendor_required_apps_managed_device" /> + <java-symbol type="array" name="vendor_required_attestation_certificates" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_user" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_device" /> @@ -2016,8 +2012,6 @@ <java-symbol type="id" name="replace_message" /> <java-symbol type="fraction" name="config_dimBehindFadeDuration" /> <java-symbol type="dimen" name="default_minimal_size_resizable_task" /> - <java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" /> - <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" /> <java-symbol type="dimen" name="task_height_of_minimized_mode" /> <java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" /> <java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" /> @@ -4399,6 +4393,7 @@ <java-symbol type="color" name="accessibility_focus_highlight_color" /> <!-- Width of the outline stroke used by the accessibility focus rectangle --> <java-symbol type="dimen" name="accessibility_focus_highlight_stroke_width" /> + <java-symbol type="dimen" name="accessibility_window_magnifier_min_size" /> <java-symbol type="bool" name="config_attachNavBarToAppDuringTransition" /> diff --git a/core/res/res/values/vendor_required_attestation_certificates.xml b/core/res/res/values/vendor_required_attestation_certificates.xml new file mode 100644 index 000000000000..ce5660f433ff --- /dev/null +++ b/core/res/res/values/vendor_required_attestation_certificates.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> +<resources> + <!-- The PEM-encoded certificates added here are used for verifying attestations. + The trustworthiness of the attestation depends on the root certificate of the chain. + + Certificates that can be used can be retrieved from: + https://developer.android.com/training/articles/security-key-attestation#root_certificate. + + If not already present in resource overlay, please add + vendor_required_attestation_certificates.xml (matching this file) in vendor overlay + with <item></item> of the PEM-encoded root certificates. + --> + <string-array translatable="false" name="vendor_required_attestation_certificates"> + </string-array> +</resources> diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml new file mode 100644 index 000000000000..552048e8be9a --- /dev/null +++ b/libs/WindowManager/Shell/res/values-television/config.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<!-- These resources are around just to allow their values to be customized + for TV products. Do not translate. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 24x24 + </string> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> +</resources> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 1b8032b7077b..d416c060c86c 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -70,4 +70,30 @@ <!-- Animation duration when exit starting window: reveal app --> <integer name="starting_window_app_reveal_anim_duration">266</integer> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 16x16 + </string> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> + + <!-- The default aspect ratio for picture-in-picture windows. --> + <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen"> + 1.777778 + </item> + + <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size + will be used instead of an adaptive size based loosely on area. --> + <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen"> + 1.777778 + </item> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 8a8231d00195..2c96786bb521 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -18,8 +18,8 @@ <dimen name="dismiss_circle_size">96dp</dimen> <dimen name="dismiss_circle_small">60dp</dimen> - <!-- The height of the gradient indicating the dismiss edge when moving a PIP. --> - <dimen name="floating_dismiss_gradient_height">250dp</dimen> + <!-- The height of the gradient indicating the dismiss edge when moving a PIP or bubble. --> + <dimen name="floating_dismiss_gradient_height">548dp</dimen> <!-- The padding around a PiP actions. --> <dimen name="pip_action_padding">16dp</dimen> @@ -129,6 +129,9 @@ <dimen name="bubble_dismiss_encircle_size">52dp</dimen> <!-- Padding around the view displayed when the bubble is expanded --> <dimen name="bubble_expanded_view_padding">16dp</dimen> + <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used + when displaying in landscape on a large screen. --> + <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen> <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include a slight touch slop around the expanded view. --> <dimen name="bubble_expanded_view_slop">8dp</dimen> @@ -251,4 +254,14 @@ <!-- The distance of the shift icon when early exit starting window. --> <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen> + + <!-- The default minimal size of a PiP task, in both dimensions. --> + <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen> + + <!-- + The overridable minimal size of a PiP task, in both dimensions. + Different from default_minimal_size_pip_resizable_task, this is to limit the dimension + when the pinned stack size is overridden by app via minWidth/minHeight. + --> + <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 75b19fb03e73..bc0db3635e35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -67,7 +67,11 @@ public class BubblePositioner { /** The max percent of screen width to use for the flyout on phone. */ public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f; /** The percent of screen width that should be used for the expanded view on a large screen. **/ - public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f; + private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f; + /** The percent of screen width that should be used for the expanded view on a large screen. **/ + private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f; + /** The percent of screen width that should be used for the expanded view on a small tablet. **/ + private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f; private Context mContext; private WindowManager mWindowManager; @@ -77,6 +81,7 @@ public class BubblePositioner { private boolean mImeVisible; private int mImeHeight; private boolean mIsLargeScreen; + private boolean mIsSmallTablet; private Rect mPositionRect; private int mDefaultMaxBubbles; @@ -86,7 +91,8 @@ public class BubblePositioner { private int mExpandedViewMinHeight; private int mExpandedViewLargeScreenWidth; - private int mExpandedViewLargeScreenInset; + private int mExpandedViewLargeScreenInsetClosestEdge; + private int mExpandedViewLargeScreenInsetFurthestEdge; private int mOverflowWidth; private int mExpandedViewPadding; @@ -127,17 +133,26 @@ public class BubblePositioner { | WindowInsets.Type.statusBars() | WindowInsets.Type.displayCutout()); - mIsLargeScreen = mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600; + final Rect bounds = windowMetrics.getBounds(); + Configuration config = mContext.getResources().getConfiguration(); + mIsLargeScreen = config.smallestScreenWidthDp >= 600; + if (mIsLargeScreen) { + float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp); + mIsSmallTablet = largestEdgeDp < 960; + } else { + mIsSmallTablet = false; + } if (BubbleDebugConfig.DEBUG_POSITIONER) { Log.w(TAG, "update positioner:" + " rotation: " + mRotation + " insets: " + insets + " isLargeScreen: " + mIsLargeScreen - + " bounds: " + windowMetrics.getBounds() + + " isSmallTablet: " + mIsSmallTablet + + " bounds: " + bounds + " showingInTaskbar: " + mShowingInTaskbar); } - updateInternal(mRotation, insets, windowMetrics.getBounds()); + updateInternal(mRotation, insets, bounds); } /** @@ -172,11 +187,31 @@ public class BubblePositioner { mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); - mExpandedViewLargeScreenWidth = (int) (bounds.width() - * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT); - mExpandedViewLargeScreenInset = mIsLargeScreen - ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2 - : mExpandedViewPadding; + if (mIsSmallTablet) { + mExpandedViewLargeScreenWidth = (int) (bounds.width() + * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT); + } else { + mExpandedViewLargeScreenWidth = isLandscape() + ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT) + : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT); + } + if (mIsLargeScreen) { + if (isLandscape() && !mIsSmallTablet) { + mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize( + R.dimen.bubble_expanded_view_largescreen_landscape_padding); + mExpandedViewLargeScreenInsetFurthestEdge = bounds.width() + - mExpandedViewLargeScreenInsetClosestEdge + - mExpandedViewLargeScreenWidth; + } else { + final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; + mExpandedViewLargeScreenInsetClosestEdge = centeredInset; + mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; + } + } else { + mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding; + mExpandedViewLargeScreenInsetFurthestEdge = mExpandedViewPadding; + } + mOverflowWidth = mIsLargeScreen ? mExpandedViewLargeScreenWidth : res.getDimensionPixelSize( @@ -328,15 +363,18 @@ public class BubblePositioner { public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) { final int pointerTotalHeight = mPointerHeight - mPointerOverlap; if (mIsLargeScreen) { + // Note: + // If we're in portrait OR if we're a small tablet, then the two insets values will + // be equal. If we're landscape and a large tablet, the two values will be different. // [left, top, right, bottom] mPaddings[0] = onLeft - ? mExpandedViewLargeScreenInset - pointerTotalHeight - : mExpandedViewLargeScreenInset; + ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight + : mExpandedViewLargeScreenInsetFurthestEdge; mPaddings[1] = 0; mPaddings[2] = onLeft - ? mExpandedViewLargeScreenInset - : mExpandedViewLargeScreenInset - pointerTotalHeight; - // Overflow doesn't show manage button / get padding from it so add padding here for it + ? mExpandedViewLargeScreenInsetFurthestEdge + : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight; + // Overflow doesn't show manage button / get padding from it so add padding here mPaddings[3] = isOverflow ? mExpandedViewPadding : 0; return mPaddings; } else { @@ -494,12 +532,13 @@ public class BubblePositioner { float x; float y; if (showBubblesVertically()) { + int inset = mExpandedViewLargeScreenInsetClosestEdge; y = rowStart + positionInRow; int left = mIsLargeScreen - ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize + ? inset - mExpandedViewPadding - mBubbleSize : mPositionRect.left; int right = mIsLargeScreen - ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding + ? mPositionRect.right - inset + mExpandedViewPadding : mPositionRect.right - mBubbleSize; x = state.onLeft ? left diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt index 74672a336161..063dac3d4109 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt @@ -16,11 +16,16 @@ package com.android.wm.shell.bubbles +import android.animation.ObjectAnimator import android.content.Context -import android.graphics.drawable.TransitionDrawable +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.util.IntProperty import android.view.Gravity import android.view.View import android.view.ViewGroup +import android.view.WindowManager +import android.view.WindowInsets import android.widget.FrameLayout import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY @@ -28,8 +33,6 @@ import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW import com.android.wm.shell.R import com.android.wm.shell.animation.PhysicsAnimator import com.android.wm.shell.common.DismissCircleView -import android.view.WindowInsets -import android.view.WindowManager /* * View that handles interactions between DismissCircleView and BubbleStackView. @@ -41,9 +44,21 @@ class DismissView(context: Context) : FrameLayout(context) { private val animator = PhysicsAnimator.getInstance(circle) private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY) - private val DISMISS_SCRIM_FADE_MS = 200 + private val DISMISS_SCRIM_FADE_MS = 200L private var wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + private var gradientDrawable = createGradient() + + private val GRADIENT_ALPHA: IntProperty<GradientDrawable> = + object : IntProperty<GradientDrawable>("alpha") { + override fun setValue(d: GradientDrawable, percent: Int) { + d.alpha = percent + } + override fun get(d: GradientDrawable): Int { + return d.alpha + } + } + init { setLayoutParams(LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -53,8 +68,7 @@ class DismissView(context: Context) : FrameLayout(context) { setClipToPadding(false) setClipChildren(false) setVisibility(View.INVISIBLE) - setBackgroundResource( - R.drawable.floating_dismiss_gradient_transition) + setBackgroundDrawable(gradientDrawable) val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size) addView(circle, LayoutParams(targetSize, targetSize, @@ -71,7 +85,11 @@ class DismissView(context: Context) : FrameLayout(context) { if (isShowing) return isShowing = true setVisibility(View.VISIBLE) - (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS) + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 255) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() + animator.cancel() animator .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring) @@ -85,7 +103,10 @@ class DismissView(context: Context) : FrameLayout(context) { fun hide() { if (!isShowing) return isShowing = false - (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS) + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 0) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() animator .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(), spring) @@ -93,6 +114,13 @@ class DismissView(context: Context) : FrameLayout(context) { .start() } + /** + * Cancels the animator for the dismiss target. + */ + fun cancelAnimators() { + animator.cancel() + } + fun updateResources() { updatePadding() layoutParams.height = resources.getDimensionPixelSize( @@ -104,6 +132,20 @@ class DismissView(context: Context) : FrameLayout(context) { circle.requestLayout() } + private fun createGradient(): GradientDrawable { + val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900) + val alpha = 0.7f * 255 + val gradientColorWithAlpha = Color.argb(alpha.toInt(), + Color.red(gradientColor), + Color.green(gradientColor), + Color.blue(gradientColor)) + val gd = GradientDrawable( + GradientDrawable.Orientation.BOTTOM_TOP, + intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT)) + gd.setAlpha(0) + return gd + } + private fun updatePadding() { val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets() val navInset = insets.getInsetsIgnoringVisibility( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index f7c92fed5522..8a482fbfa1c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -144,21 +144,42 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return Math.max(dividerInset, radius); } - /** Gets bounds of the primary split. */ + /** Gets bounds of the primary split with screen based coordinate. */ public Rect getBounds1() { return new Rect(mBounds1); } - /** Gets bounds of the secondary split. */ + /** Gets bounds of the primary split with parent based coordinate. */ + public Rect getRefBounds1() { + Rect outBounds = getBounds1(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of the secondary split with screen based coordinate. */ public Rect getBounds2() { return new Rect(mBounds2); } - /** Gets bounds of divider window. */ + /** Gets bounds of the secondary split with parent based coordinate. */ + public Rect getRefBounds2() { + final Rect outBounds = getBounds2(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of divider window with screen based coordinate. */ public Rect getDividerBounds() { return new Rect(mDividerBounds); } + /** Gets bounds of divider window with parent based coordinate. */ + public Rect getRefDividerBounds() { + final Rect outBounds = getDividerBounds(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + /** Returns leash of the current divider bar. */ @Nullable public SurfaceControl getDividerLeash() { @@ -452,14 +473,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { - t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top); + mTempRect.set(getRefDividerBounds()); + t.setPosition(dividerLeash, mTempRect.left, mTempRect.top); // Resets layer of divider bar to make sure it is always on top. t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); } - t.setPosition(leash1, mBounds1.left, mBounds1.top) - .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); - t.setPosition(leash2, mBounds2.left, mBounds2.top) - .setWindowCrop(leash2, mBounds2.width(), mBounds2.height()); + mTempRect.set(getRefBounds1()); + t.setPosition(leash1, mTempRect.left, mTempRect.top) + .setWindowCrop(leash1, mTempRect.width(), mTempRect.height()); + mTempRect.set(getRefBounds2()); + t.setPosition(leash2, mTempRect.left, mTempRect.top) + .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index e29dde2726e3..797df413d262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -32,6 +32,7 @@ import android.util.Size; import android.util.TypedValue; import android.view.Gravity; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; @@ -76,15 +77,15 @@ public class PipBoundsAlgorithm { protected void reloadResources(Context context) { final Resources res = context.getResources(); mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); + R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( - com.android.internal.R.integer.config_defaultPictureInPictureGravity); + R.integer.config_defaultPictureInPictureGravity); mDefaultMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task); + R.dimen.default_minimal_size_pip_resizable_task); mOverridableMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task); + R.dimen.overridable_minimal_size_pip_resizable_task); final String screenEdgeInsetsDpString = res.getString( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets); + R.string.config_defaultPictureInPictureScreenEdgeInsets); final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() ? Size.parseSize(screenEdgeInsetsDpString) : null; @@ -96,9 +97,9 @@ public class PipBoundsAlgorithm { mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 180e3fb48c9d..d7322ce7beda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -138,8 +138,8 @@ public class PipSurfaceTransactionHelper { // destination are different. final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; final Rect crop = mTmpDestinationRect; - crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH - : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH); + crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH + : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH); // Inverse scale for crop to fit in screen coordinates. crop.scale(1 / scale); crop.offset(insets.left, insets.top); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 60aac6806623..91615fe05417 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -97,7 +97,7 @@ public class PipTransition extends PipTransitionController { * meaningful if {@link #mInFixedRotation} is true. */ @Surface.Rotation - private int mFixedRotation; + private int mEndFixedRotation; /** Whether the PIP window has fade out for fixed rotation. */ private boolean mHasFadeOut; @@ -153,7 +153,7 @@ public class PipTransition extends PipTransitionController { final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); mInFixedRotation = fixedRotationChange != null; - mFixedRotation = mInFixedRotation + mEndFixedRotation = mInFixedRotation ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; @@ -257,7 +257,7 @@ public class PipTransition extends PipTransitionController { final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo(); if (taskInfo != null) { startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(), - new Rect(mExitDestinationBounds)); + new Rect(mExitDestinationBounds), Surface.ROTATION_0); } mExitDestinationBounds.setEmpty(); mCurrentPipTaskToken = null; @@ -332,30 +332,31 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { - TransitionInfo.Change displayRotationChange = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getMode() == TRANSIT_CHANGE - && (change.getFlags() & FLAG_IS_DISPLAY) != 0 - && change.getStartRotation() != change.getEndRotation()) { - displayRotationChange = change; - break; - } - } - - if (displayRotationChange != null) { - // Exiting PIP to fullscreen with orientation change. - startExpandAndRotationAnimation(info, startTransaction, finishTransaction, - finishCallback, displayRotationChange, pipChange); - return; - } - - // When there is no rotation, we can simply expand the PIP window. mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; + // Check if it is Shell rotation. + if (Transitions.SHELL_TRANSITIONS_ROTATION) { + TransitionInfo.Change displayRotationChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getMode() == TRANSIT_CHANGE + && (change.getFlags() & FLAG_IS_DISPLAY) != 0 + && change.getStartRotation() != change.getEndRotation()) { + displayRotationChange = change; + break; + } + } + if (displayRotationChange != null) { + // Exiting PIP to fullscreen with orientation change. + startExpandAndRotationAnimation(info, startTransaction, finishTransaction, + displayRotationChange, pipChange); + return; + } + } + // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); final Point offset = pipChange.getEndRelOffset(); @@ -364,13 +365,41 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(), destinationBounds, mPipBoundsState.getBounds()); startTransaction.apply(); - startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds); + + // Check if it is fixed rotation. + final int rotationDelta; + if (mInFixedRotation) { + final int startRotation = pipChange.getStartRotation(); + final int endRotation = mEndFixedRotation; + rotationDelta = deltaRotation(startRotation, endRotation); + final Rect endBounds = new Rect(destinationBounds); + + // Set the end frame since the display won't rotate until fixed rotation is finished + // in the next display change transition. + rotateBounds(endBounds, destinationBounds, rotationDelta); + final int degree, x, y; + if (rotationDelta == ROTATION_90) { + degree = 90; + x = destinationBounds.right; + y = destinationBounds.top; + } else { + degree = -90; + x = destinationBounds.left; + y = destinationBounds.bottom; + } + mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction, + pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y, + true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */); + } else { + rotationDelta = Surface.ROTATION_0; + } + startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds, + rotationDelta); } private void startExpandAndRotationAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change displayRotationChange, @NonNull TransitionInfo.Change pipChange) { final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(), @@ -380,11 +409,6 @@ public class PipTransition extends PipTransitionController { final CounterRotatorHelper rotator = new CounterRotatorHelper(); rotator.handleClosingChanges(info, startTransaction, displayRotationChange); - mFinishCallback = (wct, wctCB) -> { - mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); - finishCallback.onTransitionFinished(wct, wctCB); - }; - // Get the start bounds in new orientation. final Rect startBounds = new Rect(pipChange.getStartAbsBounds()); rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta); @@ -425,12 +449,11 @@ public class PipTransition extends PipTransitionController { } private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final Rect destinationBounds) { - PipAnimationController.PipTransitionAnimator animator = + final Rect destinationBounds, final int rotationDelta) { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(), destinationBounds, null, - TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0); - + TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -526,7 +549,7 @@ public class PipTransition extends PipTransitionController { mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; - final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation(); + final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation(); return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction, enterPip.getStartRotation(), endRotation); @@ -545,8 +568,8 @@ public class PipTransition extends PipTransitionController { taskInfo.pictureInPictureParams, currentBounds); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { // Need to get the bounds of new rotation in old rotation for fixed rotation, - sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation, - taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect); + computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, + destinationBounds, sourceHintRect); } PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. @@ -583,8 +606,6 @@ public class PipTransition extends PipTransitionController { startTransaction.setMatrix(leash, tmpTransform, new float[9]); } if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - // Reverse the rotation for Shell transition animation. - rotationDelta = deltaRotation(rotationDelta, 0); animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, rotationDelta); @@ -617,33 +638,22 @@ public class PipTransition extends PipTransitionController { return true; } - /** Computes destination bounds in old rotation and returns source hint rect if available. */ - @Nullable - private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation, - TaskInfo taskInfo, int direction, Rect outDestinationBounds, - @Nullable Rect sourceHintRect) { - if (direction == TRANSITION_DIRECTION_TO_PIP) { - mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); - outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); - // Transform the destination bounds to current display coordinates. - rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); - // When entering PiP (from button navigation mode), adjust the source rect hint by - // display cutout if applicable. - if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) { - if (rotationDelta == Surface.ROTATION_270) { - sourceHintRect.offset(taskInfo.displayCutoutInsets.left, - taskInfo.displayCutoutInsets.top); - } + /** Computes destination bounds in old rotation and updates source hint rect if available. */ + private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation, + TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) { + mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); + // Transform the destination bounds to current display coordinates. + rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); + // When entering PiP (from button navigation mode), adjust the source rect hint by + // display cutout if applicable. + if (outSourceHintRect != null && taskInfo.displayCutoutInsets != null) { + if (rotationDelta == Surface.ROTATION_270) { + outSourceHintRect.offset(taskInfo.displayCutoutInsets.left, + taskInfo.displayCutoutInsets.top); } - } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { - final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); - rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), - rotationDelta); - return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams, - rotatedDestinationBounds); - } - return sourceHintRect; + } } private void startExitToSplitAnimation(TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 915c5939c34b..3115f8afde3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -20,27 +20,20 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import android.content.Context; import android.content.res.Resources; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; -import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; -import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; -import android.widget.FrameLayout; import androidx.annotation.NonNull; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; -import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.DismissView; import com.android.wm.shell.common.DismissCircleView; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -56,9 +49,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen /* The multiplier to apply scale the target size by when applying the magnetic field radius */ private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f; - /** Duration of the dismiss scrim fading in/out. */ - private static final int DISMISS_TRANSITION_DURATION_MS = 200; - /** * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move * PIP. @@ -69,7 +59,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen * Container for the dismiss circle, so that it can be animated within the container via * translation rather than within the WindowManager via slow layout animations. */ - private ViewGroup mTargetViewContainer; + private DismissView mTargetViewContainer; /** Circle view used to render the dismiss target. */ private DismissCircleView mTargetView; @@ -79,16 +69,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen */ private MagnetizedObject.MagneticTarget mMagneticTarget; - /** - * PhysicsAnimator instance for animating the dismiss target in/out. - */ - private PhysicsAnimator<View> mMagneticTargetAnimator; - - /** Default configuration to use for springing the dismiss target in/out. */ - private final PhysicsAnimator.SpringConfig mTargetSpringConfig = - new PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - // Allow dragging the PIP to a location to close it private boolean mEnableDismissDragToEdge; @@ -125,12 +105,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen cleanUpDismissTarget(); } - mTargetView = new DismissCircleView(mContext); - mTargetViewContainer = new FrameLayout(mContext); - mTargetViewContainer.setBackgroundDrawable( - mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition)); - mTargetViewContainer.setClipChildren(false); - mTargetViewContainer.addView(mTargetView); + mTargetViewContainer = new DismissView(mContext); + mTargetView = mTargetViewContainer.getCircle(); mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> { if (!windowInsets.equals(mWindowInsets)) { mWindowInsets = windowInsets; @@ -187,7 +163,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen } }); - mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); } @Override @@ -213,19 +188,13 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (mTargetView == null) { return; } + if (mTargetViewContainer != null) { + mTargetViewContainer.updateResources(); + } final Resources res = mContext.getResources(); mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size); mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); - final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); - final Insets navInset = insets.getInsetsIgnoringVisibility( - WindowInsets.Type.navigationBars()); - final FrameLayout.LayoutParams newParams = - new FrameLayout.LayoutParams(mTargetSize, mTargetSize); - newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; - newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize( - R.dimen.floating_dismiss_bottom_margin); - mTargetView.setLayoutParams(newParams); // Set the magnetic field radius equal to the target size from the center of the target setMagneticFieldRadiusPercent(mMagneticFieldRadiusPercent); @@ -261,7 +230,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ public void createOrUpdateDismissTarget() { if (!mTargetViewContainer.isAttachedToWindow()) { - mMagneticTargetAnimator.cancel(); + mTargetViewContainer.cancelAnimators(); mTargetViewContainer.setVisibility(View.INVISIBLE); mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this); @@ -312,18 +281,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen createOrUpdateDismissTarget(); if (mTargetViewContainer.getVisibility() != View.VISIBLE) { - mTargetView.setTranslationY(mTargetViewContainer.getHeight()); - mTargetViewContainer.setVisibility(View.VISIBLE); mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this); - - // Cancel in case we were in the middle of animating it out. - mMagneticTargetAnimator.cancel(); - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition( - DISMISS_TRANSITION_DURATION_MS); + mTargetViewContainer.show(); } } @@ -332,16 +291,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (!mEnableDismissDragToEdge) { return; } - - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, - mTargetViewContainer.getHeight(), - mTargetSpringConfig) - .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( - DISMISS_TRANSITION_DURATION_MS); + mTargetViewContainer.hide(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 69d6c9e0c3bd..32ebe2d6aecf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -33,6 +33,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; +import android.view.WindowManagerGlobal; import androidx.annotation.Nullable; @@ -143,7 +144,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); - mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView)); } @Override @@ -164,6 +164,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top); t.apply(); } + grantPipMenuFocus(true); mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity()); } } @@ -194,8 +195,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (DEBUG) Log.d(TAG, "hideMenu()"); } - mPipMenuView.hide(mInMoveMode); + mPipMenuView.hide(); if (!mInMoveMode) { + grantPipMenuFocus(false); mDelegate.closeMenu(); } } @@ -453,4 +455,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closePip(); } + + private void grantPipMenuFocus(boolean grantFocus) { + if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus", e); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 773e9bfa8977..3090139f6db9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -30,7 +30,6 @@ import android.app.RemoteAction; import android.content.Context; import android.graphics.Rect; import android.os.Handler; -import android.os.IBinder; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -39,7 +38,6 @@ import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; -import android.view.WindowManagerGlobal; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -72,7 +70,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final ImageView mArrowRight; private final ImageView mArrowDown; private final ImageView mArrowLeft; - private IBinder mFocusGrantToken = null; private final ViewGroup mScrollView; private final ViewGroup mHorizontalScrollView; @@ -152,10 +149,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { mListener = listener; } - void setFocusGrantToken(IBinder token) { - mFocusGrantToken = token; - } - void setExpandedModeEnabled(boolean enabled) { mExpandButton.setVisibility(enabled ? VISIBLE : GONE); } @@ -170,8 +163,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { void show(boolean inMoveMode, int gravity) { if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode); - grantWindowFocus(true); - if (inMoveMode) { showMovementHints(gravity); } else { @@ -180,15 +171,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { animateAlphaTo(1, mMenuFrameView); } - void hide(boolean isInMoveMode) { + void hide() { if (DEBUG) Log.d(TAG, "hide()"); animateAlphaTo(0, mActionButtonsContainer); animateAlphaTo(0, mMenuFrameView); hideMovementHints(); - - if (!isInMoveMode) { - grantWindowFocus(false); - } } private void animateAlphaTo(float alpha, View view) { @@ -217,17 +204,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { || mArrowLeft.getAlpha() != 0f; } - private void grantWindowFocus(boolean grantFocus) { - if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); - - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - mFocusGrantToken, grantFocus); - } catch (Exception e) { - Log.e(TAG, "Unable to update focus", e); - } - } - void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) { if (DEBUG) Log.d(TAG, "setAdditionalActions()"); 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 81dacdb753a7..76641f0e6c21 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 @@ -979,7 +979,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setAlpha(dividerLeash, 1); t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); t.setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top); + mSplitLayout.getRefDividerBounds().left, + mSplitLayout.getRefDividerBounds().top); } else { t.hide(dividerLeash); } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt index 65eb9aa991c2..57bcbc093a62 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.apppairs import android.platform.test.annotations.Presubmit +import android.view.Display import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -24,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group1 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig @@ -60,7 +62,18 @@ class AppPairsTestSupportPairNonResizeableApps( // TODO pair apps through normal UX flow executeShellCommand( composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) } + val waitConditions = mutableListOf( + WindowManagerConditionsFactory.isWindowVisible(primaryApp.component), + WindowManagerConditionsFactory.isLayerVisible(primaryApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY)) + + nonResizeableApp?.let { + waitConditions.add( + WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component)) + waitConditions.add( + WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component)) + } + wmHelper.waitFor(*waitConditions.toTypedArray()) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index 0f00edea136f..cc5b9f9eb26d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -62,7 +62,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( if (wmHelper == null) { device.waitForIdle() } else { - require(wmHelper.waitImeShown()) { "IME did not appear" } + wmHelper.waitImeShown() } } @@ -79,7 +79,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper( if (wmHelper == null) { uiDevice.waitForIdle() } else { - require(wmHelper.waitImeGone()) { "IME did did not close" } + wmHelper.waitImeGone() } } else { // While pressing the back button should close the IME on TV as well, it may also lead diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index 7e232ea32181..e9d438a569d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -58,17 +58,27 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( } } - /** {@inheritDoc} */ - override fun launchViaIntent( + /** + * Launches the app through an intent instead of interacting with the launcher and waits + * until the app window is in PIP mode + */ + @JvmOverloads + fun launchViaIntentAndWaitForPip( wmHelper: WindowManagerStateHelper, - expectedWindowName: String, - action: String?, + expectedWindowName: String = "", + action: String? = null, stringExtras: Map<String, String> ) { - super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras) - wmHelper.waitPipShown() + launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras, + waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition)) } + /** + * Expand the PIP window back to full screen via intent and wait until the app is visible + */ + fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = + launchViaIntentAndWaitShown(wmHelper) + private fun focusOnObject(selector: BySelector): Boolean { // We expect all the focusable UI elements to be arranged in a way so that it is possible // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index f2c50935a3e9..274d34ba3c5b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -64,7 +64,18 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { * Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit - get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { + get() = { + setupAndTeardown(this) + setup { + eachRun { + pipApp.launchViaIntent(wmHelper) + } + } + teardown { + eachRun { + pipApp.exit(wmHelper) + } + } transitions { pipApp.clickEnterPipButton(wmHelper) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt index 4f98b70e222a..e2d08346efb6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt @@ -74,7 +74,7 @@ class ExitPipViaExpandButtonClickTest( // This will bring PipApp to fullscreen pipApp.expandPipWindowToApp(wmHelper) // Wait until the other app is no longer visible - wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) + wmHelper.waitForSurfaceAppeared(testApp.component) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index e00d7491839f..3fe6f02eccf7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -72,9 +72,9 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit } transitions { // This will bring PipApp to fullscreen - pipApp.launchViaIntent(wmHelper) + pipApp.exitPipToFullScreenViaIntent(wmHelper) // Wait until the other app is no longer visible - wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName()) + wmHelper.waitForWindowSurfaceDisappeared(testApp.component) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index bb66f7bbc01f..654fa4ec9446 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -122,15 +122,14 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { setup { test { - removeAllTasksButHome() if (!eachRun) { - pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) wmHelper.waitPipShown() } } eachRun { if (eachRun) { - pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras) + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) wmHelper.waitPipShown() } } @@ -145,7 +144,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { if (!eachRun) { pipApp.exit(wmHelper) } - removeAllTasksButHome() } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 90f898aa09da..0059846c6055 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -29,6 +29,7 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; @@ -72,16 +73,16 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void initializeMockResources() { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, DEFAULT_ASPECT_RATIO); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + R.dimen.default_minimal_size_pip_resizable_task, DEFAULT_MIN_EDGE_SIZE); res.addOverride( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, + R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); res.addOverride( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, @@ -107,7 +108,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { public void onConfigurationChanged_reloadResources() { final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; final TestableResources res = mContext.getOrCreateTestableResources(); - res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + res.addOverride(R.dimen.config_pictureInPictureDefaultAspectRatio, newDefaultAspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); @@ -463,7 +464,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultAspectRatio(float aspectRatio) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, aspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } @@ -471,7 +472,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultStackGravity(int stackGravity) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, stackGravity); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index dda1a8295e51..85527c81ad9e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -51,6 +52,7 @@ public class SplitTestUtils { final SurfaceControl leash = createMockSurface(); SplitLayout out = mock(SplitLayout.class); doReturn(dividerBounds).when(out).getDividerBounds(); + doReturn(dividerBounds).when(out).getRefDividerBounds(); doReturn(leash).when(out).getDividerLeash(); return out; } diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index e0f04a119234..9f52bf18f4e3 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -108,7 +108,6 @@ public class ImageWriter implements AutoCloseable { private long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN; private @HardwareBuffer.Format int mHardwareBufferFormat; private @NamedDataSpace int mDataSpace; - private boolean mUseLegacyImageFormat; // Field below is used by native code, do not access or modify. private int mWriterFormat; @@ -257,7 +256,6 @@ public class ImageWriter implements AutoCloseable { + ", maxImages: " + maxImages); } - mUseLegacyImageFormat = useLegacyImageFormat; // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height, @@ -334,12 +332,21 @@ public class ImageWriter implements AutoCloseable { int hardwareBufferFormat, int dataSpace, int width, int height, long usage) { mMaxImages = maxImages; mUsage = usage; - mHardwareBufferFormat = hardwareBufferFormat; - mDataSpace = dataSpace; - int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace); + int imageFormat; + // if useSurfaceImageFormatInfo is true, imageFormat will be set to UNKNOWN + // and retrieve corresponding hardwareBufferFormat and dataSpace here. + if (useSurfaceImageFormatInfo) { + imageFormat = ImageFormat.UNKNOWN; + mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat); + mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat); + } else { + imageFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace); + mHardwareBufferFormat = hardwareBufferFormat; + mDataSpace = dataSpace; + } initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false, - publicFormat, hardwareBufferFormat, dataSpace, width, height, usage); + imageFormat, hardwareBufferFormat, dataSpace, width, height, usage); } /** @@ -884,27 +891,17 @@ public class ImageWriter implements AutoCloseable { private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888; private @NamedDataSpace int mDataSpace = DataSpace.DATASPACE_UNKNOWN; private boolean mUseSurfaceImageFormatInfo = true; - // set this as true temporarily now as a workaround to get correct format - // when using surface format by default without overriding the image format - // in the builder pattern - private boolean mUseLegacyImageFormat = true; + private boolean mUseLegacyImageFormat = false; /** * Constructs a new builder for {@link ImageWriter}. * - * <p>Uses {@code surface} input parameter to retrieve image format, hal format - * and hal dataspace value for default. </p> - * * @param surface The destination Surface this writer produces Image data into. * * @throws IllegalArgumentException if the surface is already abandoned. */ public Builder(@NonNull Surface surface) { mSurface = surface; - // retrieve format from surface - mImageFormat = SurfaceUtils.getSurfaceFormat(surface); - mDataSpace = SurfaceUtils.getSurfaceDataspace(surface); - mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mImageFormat); } /** @@ -1058,11 +1055,6 @@ public class ImageWriter implements AutoCloseable { mWidth = writer.mWidth; mHeight = writer.mHeight; mDataSpace = writer.mDataSpace; - - if (!mOwner.mUseLegacyImageFormat) { - mFormat = PublicFormatUtils.getPublicFormat( - mOwner.mHardwareBufferFormat, mDataSpace); - } } @Override @@ -1083,7 +1075,7 @@ public class ImageWriter implements AutoCloseable { public int getFormat() { throwISEIfImageIsInvalid(); - if (mOwner.mUseLegacyImageFormat && mFormat == -1) { + if (mFormat == -1) { mFormat = nativeGetFormat(mDataSpace); } return mFormat; diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java index 961fab32fc2c..4ed7e19f341d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java @@ -22,6 +22,7 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCK import android.content.ContentResolver; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; @@ -34,7 +35,10 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -48,13 +52,14 @@ public final class DeviceStateRotationLockSettingsManager { private static DeviceStateRotationLockSettingsManager sSingleton; - private final String[] mDeviceStateRotationLockDefaults; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>(); private final SecureSettings mSecureSettings; + private String[] mDeviceStateRotationLockDefaults; private SparseIntArray mDeviceStateRotationLockSettings; private SparseIntArray mDeviceStateRotationLockFallbackSettings; private String mLastSettingValue; + private List<SettableDeviceState> mSettableDeviceStates; @VisibleForTesting DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) { @@ -79,6 +84,12 @@ public final class DeviceStateRotationLockSettingsManager { return sSingleton; } + /** Resets the singleton instance of this class. Only used for testing. */ + @VisibleForTesting + public static synchronized void resetInstance() { + sSingleton = null; + } + /** Returns true if device-state based rotation lock settings are enabled. */ public static boolean isDeviceStateRotationLockEnabled(Context context) { return context.getResources() @@ -186,6 +197,12 @@ public final class DeviceStateRotationLockSettingsManager { return true; } + /** Returns a list of device states and their respective auto-rotation setting availability. */ + public List<SettableDeviceState> getSettableDeviceStates() { + // Returning a copy to make sure that nothing outside can mutate our internal list. + return new ArrayList<>(mSettableDeviceStates); + } + private void initializeInMemoryMap() { String serializedSetting = mSecureSettings.getStringForUser( @@ -220,6 +237,17 @@ public final class DeviceStateRotationLockSettingsManager { } } + /** + * Resets the state of the class and saved settings back to the default values provided by the + * resources config. + */ + @VisibleForTesting + public void resetStateForTesting(Resources resources) { + mDeviceStateRotationLockDefaults = + resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults); + fallbackOnDefaults(); + } + private void fallbackOnDefaults() { loadDefaults(); persistSettings(); @@ -259,6 +287,7 @@ public final class DeviceStateRotationLockSettingsManager { } private void loadDefaults() { + mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length); mDeviceStateRotationLockSettings = new SparseIntArray( mDeviceStateRotationLockDefaults.length); mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1); @@ -279,6 +308,8 @@ public final class DeviceStateRotationLockSettingsManager { + values.length); } } + boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED; + mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable)); mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting); } catch (NumberFormatException e) { Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e); @@ -308,4 +339,38 @@ public final class DeviceStateRotationLockSettingsManager { /** Called whenever the settings have changed. */ void onSettingsChanged(); } + + /** Represents a device state and whether it has an auto-rotation setting. */ + public static class SettableDeviceState { + private final int mDeviceState; + private final boolean mIsSettable; + + SettableDeviceState(int deviceState, boolean isSettable) { + mDeviceState = deviceState; + mIsSettable = isSettable; + } + + /** Returns the device state associated with this object. */ + public int getDeviceState() { + return mDeviceState; + } + + /** Returns whether there is an auto-rotation setting for this device state. */ + public boolean isSettable() { + return mIsSettable; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SettableDeviceState)) return false; + SettableDeviceState that = (SettableDeviceState) o; + return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable; + } + + @Override + public int hashCode() { + return Objects.hash(mDeviceState, mIsSettable); + } + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java index 1a45384bc768..81006dd6b011 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java @@ -30,12 +30,17 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; +import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + @SmallTest @RunWith(AndroidJUnit4.class) public class DeviceStateRotationLockSettingsManagerTest { @@ -94,4 +99,22 @@ public class DeviceStateRotationLockSettingsManagerTest { assertThat(mNumSettingsChanges).isEqualTo(3); } + + @Test + public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() { + when(mMockResources.getStringArray( + R.array.config_perDeviceStateRotationLockDefaults)).thenReturn( + new String[]{"2:2", "4:0", "1:1", "0:0"}); + + List<SettableDeviceState> settableDeviceStates = + DeviceStateRotationLockSettingsManager.getInstance( + mMockContext).getSettableDeviceStates(); + + assertThat(settableDeviceStates).containsExactly( + new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true), + new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false), + new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true), + new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false) + ).inOrder(); + } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f0b180e5cab6..121f9e58a62a 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -588,6 +588,9 @@ <uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" /> <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" /> + <!-- Permission required for CTS test - KeyguardLockedStateApiTest --> + <uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" /> + <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> <!-- Permission required for CTS test - ResourceObserverNativeTest --> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index dad4c19799af..b98f41329c8f 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -123,6 +123,7 @@ <dimen name="bouncer_user_switcher_icon_size">190dp</dimen> <dimen name="bouncer_user_switcher_icon_size_plus_margin">222dp</dimen> + <dimen name="user_switcher_fullscreen_horizontal_gap">64dp</dimen> <dimen name="user_switcher_icon_selected_width">8dp</dimen> <dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen> <dimen name="user_switcher_fullscreen_button_padding">12dp</dimen> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml index 2d883bc1477f..6bb6c2d9877c 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml @@ -21,9 +21,8 @@ android:id="@+id/user_switcher_root" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginBottom="40dp" - android:layout_marginEnd="60dp" - android:layout_marginStart="60dp"> + android:layout_marginVertical="40dp" + android:layout_marginHorizontal="60dp"> <androidx.constraintlayout.helper.widget.Flow android:id="@+id/flow" @@ -36,7 +35,7 @@ app:flow_horizontalBias="0.5" app:flow_verticalAlign="center" app:flow_wrapMode="chain" - app:flow_horizontalGap="64dp" + app:flow_horizontalGap="@dimen/user_switcher_fullscreen_horizontal_gap" app:flow_verticalGap="44dp" app:flow_horizontalStyle="packed"/> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml index 3319442a1a68..a3d9a69e73c5 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml @@ -21,8 +21,8 @@ <ImageView android:id="@+id/user_switcher_icon" android:layout_gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin" + android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" /> <TextView style="@style/Bouncer.UserSwitcher.Spinner.Item" android:id="@+id/user_switcher_text" diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index c37c804caaa8..8a4516aaa49f 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -42,4 +42,8 @@ the shade (in alpha) --> <dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen> + <!-- Distance that the full shade transition takes in order for media to fully transition to + the shade --> + <dimen name="lockscreen_shade_media_transition_distance">200dp</dimen> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 0d20403b08f2..de03993a6f17 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -43,6 +43,7 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Log; import android.util.Range; +import android.util.Size; import android.view.Choreographer; import android.view.Display; import android.view.Gravity; @@ -62,6 +63,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.IRemoteMagnificationAnimationCallback; +import androidx.core.math.MathUtils; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; @@ -166,6 +169,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final Rect mMagnificationFrameBoundary = new Rect(); // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid. private int mSystemGestureTop = -1; + private int mMinWindowSize; private final WindowMagnificationAnimationController mAnimationController; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; @@ -208,8 +212,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mBounceEffectDuration = mResources.getInteger( com.android.internal.R.integer.config_shortAnimTime); updateDimensions(); - setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2, - mWindowBounds.height() / 2); + + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), + mWindowBounds.width() / 2, mWindowBounds.height() / 2); computeBounceAnimationScale(); mMirrorWindowControl = mirrorWindowControl; @@ -281,6 +287,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold R.dimen.magnification_drag_view_size); mOuterBorderSize = mResources.getDimensionPixelSize( R.dimen.magnification_outer_border_margin); + mMinWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); } private void computeBounceAnimationScale() { @@ -414,9 +422,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } mWindowBounds.set(currentWindowBounds); + final Size windowSize = getDefaultWindowSizeWithWindowBounds(mWindowBounds); final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); - setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY); + + setMagnificationFrame(windowSize.getWidth(), windowSize.getHeight(), (int) newCenterX, + (int) newCenterY); calculateMagnificationFrameBoundary(); return true; } @@ -454,11 +465,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWindowBounds.set(currentWindowBounds); - calculateMagnificationFrameBoundary(); - - if (!isWindowVisible()) { - return; - } // Keep MirrorWindow position on the screen unchanged when device rotates 90° // clockwise or anti-clockwise. @@ -469,14 +475,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } else if (rotationDegree == 270) { matrix.postTranslate(0, mWindowBounds.height()); } - // The rect of MirrorView is going to be transformed. - LayoutParams params = - (LayoutParams) mMirrorView.getLayoutParams(); - mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height); - final RectF transformedRect = new RectF(mTmpRect); + + final RectF transformedRect = new RectF(mMagnificationFrame); + // The window frame is going to be transformed by the rotation matrix. + transformedRect.inset(-mMirrorSurfaceMargin, -mMirrorSurfaceMargin); matrix.mapRect(transformedRect); - moveWindowMagnifier(transformedRect.left - mTmpRect.left, - transformedRect.top - mTmpRect.top); + setWindowSizeAndCenter((int) transformedRect.width(), (int) transformedRect.height(), + (int) transformedRect.centerX(), (int) transformedRect.centerY()); } /** Returns the rotation degree change of two {@link Surface.Rotation} */ @@ -573,16 +578,52 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } - private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) { + /** + * Sets the window size with given width and height in pixels without changing the + * window center. The width or the height will be clamped in the range + * [{@link #mMinWindowSize}, screen width or height]. + * + * @param width the window width in pixels + * @param height the window height in pixels. + */ + public void setWindowSize(int width, int height) { + setWindowSizeAndCenter(width, height, Float.NaN, Float.NaN); + } + + void setWindowSizeAndCenter(int width, int height, float centerX, float centerY) { + width = MathUtils.clamp(width, mMinWindowSize, mWindowBounds.width()); + height = MathUtils.clamp(height, mMinWindowSize, mWindowBounds.height()); + + if (Float.isNaN(centerX)) { + centerX = mMagnificationFrame.centerX(); + } + if (Float.isNaN(centerX)) { + centerY = mMagnificationFrame.centerY(); + } + + final int frameWidth = width - 2 * mMirrorSurfaceMargin; + final int frameHeight = height - 2 * mMirrorSurfaceMargin; + setMagnificationFrame(frameWidth, frameHeight, (int) centerX, (int) centerY); + calculateMagnificationFrameBoundary(); + // Correct the frame position to ensure it is inside the boundary. + updateMagnificationFramePosition(0, 0); + modifyWindowMagnification(true); + } + + private void setMagnificationFrame(int width, int height, int centerX, int centerY) { // Sets the initial frame area for the mirror and place it to the given center on the // display. + final int initX = centerX - width / 2; + final int initY = centerY - height / 2; + mMagnificationFrame.set(initX, initY, initX + width, initY + height); + } + + private Size getDefaultWindowSizeWithWindowBounds(Rect windowBounds) { int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2; initSize = Math.min(mResources.getDimensionPixelSize(R.dimen.magnification_max_frame_size), initSize); initSize += 2 * mMirrorSurfaceMargin; - final int initX = centerX - initSize / 2; - final int initY = centerY - initSize / 2; - mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); + return new Size(initSize, initSize); } /** @@ -596,8 +637,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } mTransaction.show(mMirrorSurface) .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl()); - - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } private void addDragTouchListeners() { @@ -615,18 +655,25 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } /** - * Modifies the placement of the mirrored content when the position of mMirrorView is updated. + * Modifies the placement of the mirrored content when the position or size of mMirrorView is + * updated. + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void modifyWindowMagnification(SurfaceControl.Transaction t) { + private void modifyWindowMagnification(boolean computeWindowSize) { mSfVsyncFrameProvider.postFrameCallback(mMirrorViewGeometryVsyncCallback); - updateMirrorViewLayout(); + updateMirrorViewLayout(computeWindowSize); } /** - * Updates the layout params of MirrorView and translates MirrorView position when the view is - * moved close to the screen edges. + * Updates the layout params of MirrorView based on the size of {@link #mMagnificationFrame} + * and translates MirrorView position when the view is moved close to the screen edges; + * + * @param computeWindowSize set to {@code true} to compute window size with + * {@link #mMagnificationFrame}. */ - private void updateMirrorViewLayout() { + private void updateMirrorViewLayout(boolean computeWindowSize) { if (!isWindowVisible()) { return; } @@ -637,6 +684,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold (LayoutParams) mMirrorView.getLayoutParams(); params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; + if (computeWindowSize) { + params.width = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin; + params.height = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin; + } // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView // able to move close to the screen edges. @@ -899,7 +950,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold createMirrorWindow(); showControls(); } else { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -930,7 +981,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return; } if (updateMagnificationFramePosition((int) offsetX, (int) offsetY)) { - modifyWindowMagnification(mTransaction); + modifyWindowMagnification(false); } } @@ -1014,9 +1065,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); pw.println(" mScale:" + mScale); pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mMagnificationFrameBoundary:" + + (isWindowVisible() ? mMagnificationFrameBoundary : "empty")); + pw.println(" mMagnificationFrame:" + + (isWindowVisible() ? mMagnificationFrame : "empty")); pw.println(" mSourceBounds:" + (isWindowVisible() ? mSourceBounds : "empty")); pw.println(" mSystemGestureTop:" + mSystemGestureTop); + pw.println(" mMagnificationFrameOffsetX:" + mMagnificationFrameOffsetX); + pw.println(" mMagnificationFrameOffsetY:" + mMagnificationFrameOffsetY); } private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index be76e8fcae37..745010372182 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -18,12 +18,9 @@ package com.android.systemui.dreams; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import android.graphics.Rect; -import android.graphics.Region; import android.os.Handler; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; @@ -62,43 +59,6 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve // Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates). private final Handler mHandler; - // A hook into the internal inset calculation where we declare the overlays as the only - // touchable regions. - private final ViewTreeObserver.OnComputeInternalInsetsListener - mOnComputeInternalInsetsListener = - new ViewTreeObserver.OnComputeInternalInsetsListener() { - @Override - public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - final Region region = new Region(); - final Rect rect = new Rect(); - final int childCount = mDreamOverlayContentView.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mDreamOverlayContentView.getChildAt(i); - - if (mComplicationHostViewController.getView() == child) { - region.op(mComplicationHostViewController.getTouchRegions(), - Region.Op.UNION); - continue; - } - - if (child.getGlobalVisibleRect(rect)) { - region.op(rect, Region.Op.UNION); - } - } - - // Add the notifications drag area to the tap region (otherwise the - // notifications shade can't be dragged down). - if (mDreamOverlayContentView.getGlobalVisibleRect(rect)) { - rect.bottom = rect.top + mDreamOverlayNotificationsDragAreaHeight; - region.op(rect, Region.Op.UNION); - } - - inoutInfo.touchableRegion.set(region); - } - }; - @Inject public DreamOverlayContainerViewController( DreamOverlayContainerView containerView, @@ -136,16 +96,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve @Override protected void onViewAttached() { - mView.getViewTreeObserver() - .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); mHandler.postDelayed(this::updateBurnInOffsets, mBurnInProtectionUpdateInterval); } @Override protected void onViewDetached() { mHandler.removeCallbacks(this::updateBurnInOffsets); - mView.getViewTreeObserver() - .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); } View getContainerView() { @@ -162,6 +118,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve // so no translation occurs when the values don't change. mView.setTranslationX(getBurnInOffset(mMaxBurnInOffset * 2, true) - mMaxBurnInOffset); + mView.setTranslationY(getBurnInOffset(mMaxBurnInOffset * 2, false) - mMaxBurnInOffset); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index ed82ab0e308f..32b2309ee83b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -24,6 +24,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import com.android.systemui.dreams.dagger.DreamOverlayComponent; +import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.ViewController; import java.lang.annotation.Retention; @@ -48,6 +49,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private static final int WIFI_STATUS_AVAILABLE = 2; private final ConnectivityManager mConnectivityManager; + private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() .clearCapabilities() @@ -77,9 +79,11 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve @Inject public DreamOverlayStatusBarViewController( DreamOverlayStatusBarView view, - ConnectivityManager connectivityManager) { + ConnectivityManager connectivityManager, + TouchInsetManager.TouchInsetSession touchInsetSession) { super(view); mConnectivityManager = connectivityManager; + mTouchInsetSession = touchInsetSession; } @Override @@ -92,11 +96,13 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve onWifiAvailabilityChanged( capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)); + mTouchInsetSession.addViewToTracking(mView); } @Override protected void onViewDetached() { mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mTouchInsetSession.clear(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java index 0b80d8a87879..437e799106c5 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java @@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Constraints; import com.android.systemui.R; +import com.android.systemui.touch.TouchInsetManager; import java.util.ArrayList; import java.util.Collections; @@ -52,6 +53,7 @@ public class ComplicationLayoutEngine { private static class ViewEntry implements Comparable<ViewEntry> { private final View mView; private final ComplicationLayoutParams mLayoutParams; + private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final Parent mParent; @Complication.Category private final int mCategory; @@ -61,7 +63,8 @@ public class ComplicationLayoutEngine { * Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding * view hierarchy to be accessed without traversing the entire view tree. */ - ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent, + ViewEntry(View view, ComplicationLayoutParams layoutParams, + TouchInsetManager.TouchInsetSession touchSession, int category, Parent parent, int margin) { mView = view; // Views that are generated programmatically do not have a unique id assigned to them @@ -70,9 +73,12 @@ public class ComplicationLayoutEngine { // {@link Complication.ViewHolder} should not reference the root container by id. mView.setId(View.generateViewId()); mLayoutParams = layoutParams; + mTouchInsetSession = touchSession; mCategory = category; mParent = parent; mMargin = margin; + + touchSession.addViewToTracking(mView); } /** @@ -217,6 +223,7 @@ public class ComplicationLayoutEngine { mParent.removeEntry(this); ((ViewGroup) mView.getParent()).removeView(mView); + mTouchInsetSession.removeViewFromTracking(mView); } @Override @@ -242,15 +249,18 @@ public class ComplicationLayoutEngine { */ private static class Builder { private final View mView; + private final TouchInsetManager.TouchInsetSession mTouchSession; private final ComplicationLayoutParams mLayoutParams; private final int mCategory; private Parent mParent; private int mMargin; - Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) { + Builder(View view, TouchInsetManager.TouchInsetSession touchSession, + ComplicationLayoutParams lp, @Complication.Category int category) { mView = view; mLayoutParams = lp; mCategory = category; + mTouchSession = touchSession; } /** @@ -291,7 +301,8 @@ public class ComplicationLayoutEngine { * Builds and returns the resulting {@link ViewEntry}. */ ViewEntry build() { - return new ViewEntry(mView, mLayoutParams, mCategory, mParent, mMargin); + return new ViewEntry(mView, mLayoutParams, mTouchSession, mCategory, mParent, + mMargin); } } @@ -442,13 +453,16 @@ public class ComplicationLayoutEngine { private final int mMargin; private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>(); private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>(); + private final TouchInsetManager.TouchInsetSession mSession; /** */ @Inject public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout, - @Named(COMPLICATION_MARGIN) int margin) { + @Named(COMPLICATION_MARGIN) int margin, + TouchInsetManager.TouchInsetSession session) { mLayout = layout; mMargin = margin; + mSession = session; } /** @@ -468,7 +482,7 @@ public class ComplicationLayoutEngine { removeComplication(id); } - final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category) + final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, mSession, lp, category) .setMargin(mMargin); // Add position group if doesn't already exist diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 839a05e6e78f..63676d629cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -29,6 +29,9 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayContainerView; import com.android.systemui.dreams.DreamOverlayStatusBarView; +import com.android.systemui.touch.TouchInsetManager; + +import java.util.concurrent.Executor; import javax.inject.Named; @@ -65,6 +68,21 @@ public abstract class DreamOverlayModule { /** */ @Provides + public static TouchInsetManager.TouchInsetSession providesTouchInsetSession( + TouchInsetManager manager) { + return manager.createSession(); + } + + /** */ + @Provides + @DreamOverlayComponent.DreamOverlayScope + public static TouchInsetManager providesTouchInsetManager(@Main Executor executor, + DreamOverlayContainerView view) { + return new TouchInsetManager(executor, view); + } + + /** */ + @Provides @DreamOverlayComponent.DreamOverlayScope public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView( DreamOverlayContainerView view) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index eee39552bcc8..f4b6fbd0324f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -795,6 +795,9 @@ class MediaHierarchyManager @Inject constructor( @TransformationType fun calculateTransformationType(): Int { if (isTransitioningToFullShade) { + if (inSplitShade) { + return TRANSFORMATION_TYPE_TRANSITION + } return TRANSFORMATION_TYPE_FADE } if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS || @@ -961,6 +964,7 @@ class MediaHierarchyManager @Inject constructor( (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS !hasActiveMedia -> LOCATION_QS + onLockscreen && isSplitShadeExpanding() -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS @@ -986,6 +990,10 @@ class MediaHierarchyManager @Inject constructor( return location } + private fun isSplitShadeExpanding(): Boolean { + return inSplitShade && isTransitioningToFullShade + } + /** * Are we currently transforming to the full shade and already in QQS */ @@ -993,6 +1001,10 @@ class MediaHierarchyManager @Inject constructor( if (!isTransitioningToFullShade) { return false } + if (inSplitShade) { + // Split shade doesn't use QQS. + return false + } return fullShadeTransitionProgress > 0.5f } @@ -1000,6 +1012,10 @@ class MediaHierarchyManager @Inject constructor( * Is the current transformationType fading */ private fun isCurrentlyFading(): Boolean { + if (isSplitShadeExpanding()) { + // Split shade always uses transition instead of fade. + return false + } if (isTransitioningToFullShade) { return true } diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt index a262b8ad2e28..217210a5dafc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt @@ -165,6 +165,7 @@ internal class FooterActionsController @Inject constructor( powerMenuLite.visibility = View.GONE } settingsButton.setOnClickListener(onClickListener) + multiUserSetting.isListening = true if (featureFlags.isEnabled(Flags.NEW_FOOTER)) { val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout securityFootersContainer?.addView(securityFooter) @@ -215,6 +216,7 @@ internal class FooterActionsController @Inject constructor( override fun onViewDetached() { setListening(false) + multiUserSetting.isListening = false } fun setListening(listening: Boolean) { @@ -222,7 +224,6 @@ internal class FooterActionsController @Inject constructor( return } this.listening = listening - multiUserSetting.isListening = listening if (this.listening) { userInfoController.addCallback(onUserInfoChangedListener) updateView() diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 3c7933f0d218..3ef72202a591 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -513,7 +513,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mContainer.setExpansion(expansion); final float translationScaleY = (mInSplitShade ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); - boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard; + boolean onKeyguard = isKeyguardState(); + boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard; if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( onKeyguardAndExpanded @@ -547,6 +548,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mHeader.updateResources(); } } + mQSPanelController.setIsOnKeyguard(onKeyguard); mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5126fcb4c34d..b04d75273831 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -110,6 +110,8 @@ public class QSPanel extends LinearLayout implements Tunable { private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>(); private final Rect mClippingRect = new Rect(); private boolean mUseNewFooter = false; + private ViewGroup mMediaHostView; + private boolean mShouldMoveMediaOnExpansion = true; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -289,9 +291,15 @@ public class QSPanel extends LinearLayout implements Tunable { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (move) { + int topOffset; + if (child == mMediaHostView && !mShouldMoveMediaOnExpansion) { + topOffset = 0; + } else { + topOffset = tileHeightOffset; + } int top = Objects.requireNonNull(mChildrenLayoutTop.get(child)); - child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset, - child.getRight(), top + tileHeightOffset + child.getHeight()); + child.setLeftTopRightBottom(child.getLeft(), top + topOffset, + child.getRight(), top + topOffset + child.getHeight()); } if (child == mTileLayout) { move = true; @@ -463,6 +471,7 @@ public class QSPanel extends LinearLayout implements Tunable { if (!mUsingMediaPlayer) { return; } + mMediaHostView = hostView; ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this; ViewGroup currentParent = (ViewGroup) hostView.getParent(); if (currentParent != newParent) { @@ -656,6 +665,19 @@ public class QSPanel extends LinearLayout implements Tunable { updatePadding(); } + /** + * Sets whether the media container should move during the expansion of the QS Panel. + * + * As the QS Panel expands and the QS unsquish, the views below the QS tiles move to adapt to + * the new height of the QS tiles. + * + * In some cases this might not be wanted for media. One example is when there is a transition + * animation of the media container happening on split shade lock screen. + */ + public void setShouldMoveMediaOnExpansion(boolean shouldMoveMediaOnExpansion) { + mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion; + } + private class H extends Handler { private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 3172aa9592dd..6572daa91269 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -419,6 +419,16 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr return mView.getBrightnessView(); } + /** Sets whether we are currently on lock screen. */ + public void setIsOnKeyguard(boolean isOnKeyguard) { + boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard; + // When the split shade is expanding on lockscreen, the media container transitions from the + // lockscreen to QS. + // We have to prevent the media container position from moving during the transition to have + // a smooth translation animation without stuttering. + mView.setShouldMoveMediaOnExpansion(!isOnSplitShadeLockscreen); + } + /** */ public static final class TileRecord { public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 8366bddaab9f..8a9d6ddb2e14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -342,9 +342,7 @@ class LockscreenShadeTransitionController @Inject constructor( qS.setTransitionToFullShadeAmount(field, qSDragProgress) notificationPanelController.setTransitionToFullShadeAmount(field, false /* animate */, 0 /* delay */) - // TODO: appear media also in split shade - val mediaAmount = if (useSplitShade) 0f else field - mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount) + mediaHierarchyManager.setTransitionToFullShadeAmount(field) transitionToShadeAmountCommon(field) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 9f9e7d9a276e..5caf4f604fcd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB import android.app.IActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Region; import android.os.Binder; @@ -117,6 +118,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW * @see #batchApplyWindowLayoutParams(Runnable) */ private int mDeferWindowLayoutParams; + private boolean mLastKeyguardRotationAllowed; @Inject public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager, @@ -143,7 +145,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mScreenOffAnimationController = screenOffAnimationController; dumpManager.registerDumpable(getClass().getName(), this); mAuthController = authController; - + mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed(); mLockScreenDisplayTimeout = context.getResources() .getInteger(R.integer.config_lockScreenDisplayTimeout); ((SysuiStatusBarStateController) statusBarStateController) @@ -779,6 +781,17 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW setKeyguardDark(useDarkText); } + @Override + public void onConfigChanged(Configuration newConfig) { + final boolean newScreenRotationAllowed = mKeyguardStateController + .isKeyguardScreenRotationAllowed(); + + if (mLastKeyguardRotationAllowed != newScreenRotationAllowed) { + apply(mCurrentState); + mLastKeyguardRotationAllowed = newScreenRotationAllowed; + } + } + /** * When keyguard will be dismissed but didn't start animation yet. */ diff --git a/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java new file mode 100644 index 000000000000..de4e1e2f5886 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touch/TouchInsetManager.java @@ -0,0 +1,181 @@ +/* + * 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 com.android.systemui.touch; + +import android.graphics.Rect; +import android.graphics.Region; +import android.view.View; +import android.view.ViewRootImpl; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.Executor; + +/** + * {@link TouchInsetManager} handles setting the touchable inset regions for a given View. This + * is useful for passing through touch events for all but select areas. + */ +public class TouchInsetManager { + /** + * {@link TouchInsetSession} provides an individualized session with the + * {@link TouchInsetManager}, linking any action to the client. + */ + public static class TouchInsetSession { + private final TouchInsetManager mManager; + + private final HashSet<View> mTrackedViews; + private final Executor mExecutor; + + private final View.OnLayoutChangeListener mOnLayoutChangeListener = + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) + -> updateTouchRegion(); + + /** + * Default constructor + * @param manager The parent {@link TouchInsetManager} which will be affected by actions on + * this session. + * @param rootView The parent of views that will be tracked. + * @param executor An executor for marshalling operations. + */ + TouchInsetSession(TouchInsetManager manager, Executor executor) { + mManager = manager; + mTrackedViews = new HashSet<>(); + mExecutor = executor; + } + + /** + * Adds a descendant of the root view to be tracked. + * @param view {@link View} to be tracked. + */ + public void addViewToTracking(View view) { + mExecutor.execute(() -> { + mTrackedViews.add(view); + view.addOnLayoutChangeListener(mOnLayoutChangeListener); + updateTouchRegion(); + }); + } + + /** + * Removes a view from further tracking + * @param view {@link View} to be removed. + */ + public void removeViewFromTracking(View view) { + mExecutor.execute(() -> { + mTrackedViews.remove(view); + view.removeOnLayoutChangeListener(mOnLayoutChangeListener); + updateTouchRegion(); + }); + } + + private void updateTouchRegion() { + final Region cumulativeRegion = Region.obtain(); + + mTrackedViews.stream().forEach(view -> { + final Rect boundaries = new Rect(); + view.getBoundsOnScreen(boundaries); + cumulativeRegion.op(boundaries, Region.Op.UNION); + }); + + mManager.setTouchRegion(this, cumulativeRegion); + + cumulativeRegion.recycle(); + } + + /** + * Removes all tracked views and updates insets accordingly. + */ + public void clear() { + mExecutor.execute(() -> { + mManager.clearRegion(this); + mTrackedViews.clear(); + }); + } + } + + private final HashMap<TouchInsetSession, Region> mDefinedRegions = new HashMap<>(); + private final Executor mExecutor; + private final View mRootView; + + private final View.OnAttachStateChangeListener mAttachListener = + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + updateTouchInset(); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }; + + /** + * Default constructor. + * @param executor An {@link Executor} to marshal all operations on. + * @param rootView The root {@link View} for all views in sessions. + */ + public TouchInsetManager(Executor executor, View rootView) { + mExecutor = executor; + mRootView = rootView; + mRootView.addOnAttachStateChangeListener(mAttachListener); + + } + + /** + * Creates a new associated session. + */ + public TouchInsetSession createSession() { + return new TouchInsetSession(this, mExecutor); + } + + private void updateTouchInset() { + final ViewRootImpl viewRootImpl = mRootView.getViewRootImpl(); + + if (viewRootImpl == null) { + return; + } + + final Region aggregateRegion = Region.obtain(); + + for (Region region : mDefinedRegions.values()) { + aggregateRegion.op(region, Region.Op.UNION); + } + + viewRootImpl.setTouchableRegion(aggregateRegion); + + aggregateRegion.recycle(); + } + + protected void setTouchRegion(TouchInsetSession session, Region region) { + final Region introducedRegion = Region.obtain(region); + mExecutor.execute(() -> { + mDefinedRegions.put(session, introducedRegion); + updateTouchInset(); + }); + } + + private void clearRegion(TouchInsetSession session) { + mExecutor.execute(() -> { + final Region storedRegion = mDefinedRegions.remove(session); + + if (storedRegion != null) { + storedRegion.recycle(); + } + + updateTouchInset(); + }); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index 14585fb0f58e..c0d7925cf2bb 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -35,9 +35,8 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.ImageView import android.widget.TextView - import androidx.constraintlayout.helper.widget.Flow - +import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.UserIcons import com.android.settingslib.Utils import com.android.systemui.R @@ -47,12 +46,12 @@ import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.statusbar.phone.ShadeController import com.android.systemui.statusbar.policy.UserSwitcherController import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter -import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA +import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord import com.android.systemui.util.LifecycleActivity - import javax.inject.Inject +import kotlin.math.ceil private const val USER_VIEW = "user_view" @@ -137,6 +136,18 @@ class UserSwitcherActivity @Inject constructor( return UserIcons.getDefaultUserIcon(resources, item.info.id, false) } + fun getTotalUserViews(): Int { + return users.count { item -> + !doNotRenderUserView(item) + } + } + + fun doNotRenderUserView(item: UserRecord): Boolean { + return item.isAddUser || + item.isAddSupervisedUser || + item.isGuest && item.info == null + } + private fun getDrawable(item: UserRecord): Drawable { var drawable = if (item.isCurrent && item.isGuest) { getDrawable(R.drawable.ic_avatar_guest_user) @@ -211,7 +222,8 @@ class UserSwitcherActivity @Inject constructor( userSwitcherController.init(parent) initBroadcastReceiver() - buildUserViews() + + parent.post { buildUserViews() } } private fun showPopupMenu() { @@ -272,16 +284,32 @@ class UserSwitcherActivity @Inject constructor( } parent.removeViews(start, count) addUserRecords.clear() - val flow = requireViewById<Flow>(R.id.flow) + val totalWidth = parent.width + val userViewCount = adapter.getTotalUserViews() + val maxColumns = getMaxColumns(userViewCount) + val horizontalGap = resources + .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap) + val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap + val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns + + flow.setMaxElementsWrap(maxColumns) + for (i in 0 until adapter.getCount()) { val item = adapter.getItem(i) - if (item.isAddUser || - item.isAddSupervisedUser || - item.isGuest && item.info == null) { + if (adapter.doNotRenderUserView(item)) { addUserRecords.add(item) } else { val userView = adapter.getView(i, null, parent) + userView.requireViewById<ImageView>(R.id.user_switcher_icon).apply { + val lp = layoutParams + if (maxWidgetDiameter < lp.width) { + lp.width = maxWidgetDiameter + lp.height = maxWidgetDiameter + layoutParams = lp + } + } + userView.setId(View.generateViewId()) parent.addView(userView) @@ -333,6 +361,11 @@ class UserSwitcherActivity @Inject constructor( broadcastDispatcher.registerReceiver(broadcastReceiver, filter) } + @VisibleForTesting + fun getMaxColumns(userCount: Int): Int { + return if (userCount < 5) 4 else ceil(userCount / 2.0).toInt() + } + private class ItemAdapter( val parentContext: Context, val resource: Int, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 1dd5e227a909..6e5926db519d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -88,6 +88,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @LargeTest @TestableLooper.RunWithLooper @@ -345,15 +346,17 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test public void onOrientationChanged_disabled_updateDisplayRotation() { - final Display display = Mockito.spy(mContext.getDisplay()); - when(display.getRotation()).thenReturn(Surface.ROTATION_90); - when(mContext.getDisplay()).thenReturn(display); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window clockwise 90 degree. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); + final int newRotation = simulateRotateTheDevice(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); - }); + mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged( + ActivityInfo.CONFIG_ORIENTATION)); - assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); + assertEquals(newRotation, mWindowMagnificationController.mRotation); } @Test @@ -603,6 +606,113 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag()); } + @Test + public void setMinimumWindowSize_enabled_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setMinimumWindowSizeThenEnable_expectedWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final int expectedWindowHeight = minimumWindowSize; + final int expectedWindowWidth = minimumWindowSize; + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight); + mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(expectedWindowHeight, actualWindowHeight.get()); + assertEquals(expectedWindowWidth, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLessThanMin_enabled_minimumWindowSize() { + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(minimumWindowSize - 10, + minimumWindowSize - 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(minimumWindowSize, actualWindowHeight.get()); + assertEquals(minimumWindowSize, actualWindowWidth.get()); + } + + @Test + public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger actualWindowHeight = new AtomicInteger(); + final AtomicInteger actualWindowWidth = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10); + actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height); + actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width); + }); + + assertEquals(bounds.height(), actualWindowHeight.get()); + assertEquals(bounds.width(), actualWindowWidth.get()); + } + + @Test + public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() { + + final int minimumWindowSize = mResources.getDimensionPixelSize( + com.android.internal.R.dimen.accessibility_window_magnifier_min_size); + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, Float.NaN)); + + final AtomicInteger magnificationCenterX = new AtomicInteger(); + final AtomicInteger magnificationCenterY = new AtomicInteger(); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize, + minimumWindowSize, bounds.right, bounds.bottom); + magnificationCenterX.set((int) mWindowMagnificationController.getCenterX()); + magnificationCenterY.set((int) mWindowMagnificationController.getCenterY()); + }); + + assertTrue(magnificationCenterX.get() < bounds.right); + assertTrue(magnificationCenterY.get() < bounds.bottom); + } + private CharSequence getAccessibilityWindowTitle() { final View mirrorView = mWindowManager.getAttachedView(); if (mirrorView == null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 7af039b7fa06..8ce10b808f32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -17,7 +17,6 @@ package com.android.systemui.dreams; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -118,31 +117,6 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { } @Test - public void testOnViewAttachedRegistersComputeInsetsListener() { - mController.onViewAttached(); - verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any()); - } - - @Test - public void testOnViewDetachedUnregistersComputeInsetsListener() { - mController.onViewDetached(); - verify(mViewTreeObserver).removeOnComputeInternalInsetsListener(any()); - } - - @Test - public void testComputeInsetsListenerReturnsRegion() { - final ArgumentCaptor<ViewTreeObserver.OnComputeInternalInsetsListener> - computeInsetsListenerCapture = - ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener.class); - mController.onViewAttached(); - verify(mViewTreeObserver).addOnComputeInternalInsetsListener( - computeInsetsListenerCapture.capture()); - final ViewTreeObserver.InternalInsetsInfo info = new ViewTreeObserver.InternalInsetsInfo(); - computeInsetsListenerCapture.getValue().onComputeInternalInsets(info); - assertNotNull(info.touchableRegion); - } - - @Test public void testBurnInProtectionStartsWhenContentViewAttached() { mController.onViewAttached(); verify(mHandler).postDelayed(any(Runnable.class), eq(BURN_IN_PROTECTION_UPDATE_INTERVAL)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 58ffbfa76328..15aaf5fa8e42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -163,6 +163,31 @@ public class DreamOverlayServiceTest extends SysuiTestCase { } @Test + public void testPreviewModeFalseByDefault() { + mService.onBind(new Intent()); + + assertThat(mService.isPreviewMode()).isFalse(); + } + + @Test + public void testPreviewModeSetByIntentExtra() { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_IS_PREVIEW, true); + mService.onBind(intent); + + assertThat(mService.isPreviewMode()).isTrue(); + } + + @Test + public void testDreamLabel() { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_DREAM_LABEL, "TestDream"); + mService.onBind(intent); + + assertThat(mService.getDreamLabel()).isEqualTo("TestDream"); + } + + @Test public void testDestroy() { mService.onDestroy(); mMainExecutor.runAllReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index 658702929d7b..ad8d44d62a5c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.touch.TouchInsetManager; import org.junit.Before; import org.junit.Test; @@ -49,13 +50,16 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { NetworkCapabilities mNetworkCapabilities; @Mock Network mNetwork; + @Mock + TouchInsetManager.TouchInsetSession mTouchSession; DreamOverlayStatusBarViewController mController; @Before public void setup() { MockitoAnnotations.initMocks(this); - mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager); + mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager, + mTouchSession); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java index 64b267d6e142..51dcf2ec18f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java @@ -29,6 +29,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.touch.TouchInsetManager; import org.junit.Before; import org.junit.Test; @@ -46,6 +47,9 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { @Mock ConstraintLayout mLayout; + @Mock + TouchInsetManager.TouchInsetSession mTouchSession; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -112,7 +116,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { Complication.CATEGORY_STANDARD, mLayout); - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, 0, mTouchSession); addComplication(engine, firstViewInfo); // Ensure the view is added to the top end corner @@ -139,7 +144,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { Complication.CATEGORY_STANDARD, mLayout); - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, 0, mTouchSession); addComplication(engine, firstViewInfo); // Ensure the view is added to the top end corner @@ -155,7 +161,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { */ @Test public void testDirectionLayout() { - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, 0, mTouchSession); final ViewInfo firstViewInfo = new ViewInfo( new ComplicationLayoutParams( @@ -203,7 +210,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { */ @Test public void testPositionLayout() { - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, 0, mTouchSession); final ViewInfo firstViewInfo = new ViewInfo( new ComplicationLayoutParams( @@ -290,7 +298,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { @Test public void testMargin() { final int margin = 5; - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, margin); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, margin, mTouchSession); final ViewInfo firstViewInfo = new ViewInfo( new ComplicationLayoutParams( @@ -364,7 +373,8 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { */ @Test public void testRemoval() { - final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0); + final ComplicationLayoutEngine engine = + new ComplicationLayoutEngine(mLayout, 0, mTouchSession); final ViewInfo firstViewInfo = new ViewInfo( new ComplicationLayoutParams( diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index e606be179cc6..b359ae5317b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -33,10 +33,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView -import junit.framework.Assert +import com.android.systemui.util.mockito.any +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Rule @@ -44,16 +45,16 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyLong import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` -import org.mockito.Mockito.any -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.anyLong import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -83,8 +84,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Mock private lateinit var keyguardViewController: KeyguardViewController @Mock - private lateinit var configurationController: ConfigurationController - @Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView @Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController @@ -97,6 +96,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { val mockito = MockitoJUnit.rule() private lateinit var mediaHiearchyManager: MediaHierarchyManager private lateinit var mediaFrame: ViewGroup + private val configurationController = FakeConfigurationController() @Before fun setup() { @@ -176,12 +176,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Test fun testGoingToFullShade() { - // Let's set it onto Lock screen - `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( - true) - statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) - clearInvocations(mediaCarouselController) + goToLockscreen() // Let's transition all the way to full shade mediaHiearchyManager.setTransitionToFullShadeAmount(100000f) @@ -204,41 +199,48 @@ class MediaHierarchyManagerTest : SysuiTestCase() { // Let's make sure alpha is set mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f) - Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f) + assertThat(mediaFrame.alpha).isNotEqualTo(1.0f) } @Test fun testTransformationOnLockScreenIsFading() { - // Let's set it onto Lock screen - `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( - true) - statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) - clearInvocations(mediaCarouselController) + goToLockscreen() + expandQS() + + val transformType = mediaHiearchyManager.calculateTransformationType() + assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + } + + @Test + fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() { + enableSplitShade() + goToLockscreen() + expandQS() + mediaHiearchyManager.setTransitionToFullShadeAmount(10000f) + + val transformType = mediaHiearchyManager.calculateTransformationType() + assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION) + } + + @Test + fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() { + enableSplitShade() + goToLockscreen() + goToLockedShade() + expandQS() + mediaHiearchyManager.setTransitionToFullShadeAmount(0f) - // Let's transition from lockscreen to qs - mediaHiearchyManager.qsExpansion = 1.0f val transformType = mediaHiearchyManager.calculateTransformationType() - Assert.assertTrue("media isn't transforming to qs with a fade", - transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) } @Test fun testTransformationOnLockScreenToQQSisFading() { - // Let's set it onto Lock screen - `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( - true) - statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) - clearInvocations(mediaCarouselController) + goToLockscreen() + goToLockedShade() - // Let's transition from lockscreen to qs - `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) - statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD, - StatusBarState.SHADE_LOCKED) val transformType = mediaHiearchyManager.calculateTransformationType() - Assert.assertTrue("media isn't transforming to qqswith a fade", - transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) } @Test @@ -254,4 +256,32 @@ class MediaHierarchyManagerTest : SysuiTestCase() { verify(mediaCarouselController).closeGuts() } -}
\ No newline at end of file + + private fun enableSplitShade() { + context.getOrCreateTestableResources().addOverride( + R.bool.config_use_split_notification_shade, true + ) + configurationController.notifyConfigurationChanged() + } + + private fun goToLockscreen() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true + ) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + } + + private fun goToLockedShade() { + whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + statusBarCallback.value.onStatePreChange( + StatusBarState.KEYGUARD, + StatusBarState.SHADE_LOCKED + ) + } + + private fun expandQS() { + mediaHiearchyManager.qsExpansion = 1.0f + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt index 8ce50a680e50..f736f26e5c46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt @@ -100,7 +100,9 @@ class FooterActionsControllerTest : LeakCheckedTest() { @After fun tearDown() { - ViewUtils.detachView(view) + if (view.isAttachedToWindow) { + ViewUtils.detachView(view) + } } @Test @@ -139,8 +141,7 @@ class FooterActionsControllerTest : LeakCheckedTest() { @Test fun testMultiUserSwitchUpdatedWhenSettingChanged() { - // When expanded, listening is true - controller.setListening(true) + // Always listening to setting while View is attached testableLooper.processAllMessages() val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) @@ -156,4 +157,24 @@ class FooterActionsControllerTest : LeakCheckedTest() { assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE) } + + @Test + fun testMultiUserSettingNotListenedAfterDetach() { + testableLooper.processAllMessages() + + val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) + assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) + + ViewUtils.detachView(view) + + // The setting is only used as an indicator for whether the view should refresh. The actual + // value of the setting is ignored; isMultiUserEnabled is the source of truth + whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true) + + // Changing the value of USER_SWITCHER_ENABLED should cause the view to update + fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId) + testableLooper.processAllMessages() + + assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 9076e1607be5..75ccd8f03a01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -23,7 +23,7 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBar -import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.FakeConfigurationController import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -66,7 +66,6 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager @Mock lateinit var scrimController: ScrimController - @Mock lateinit var configurationController: ConfigurationController @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var notificationPanelController: NotificationPanelViewController @Mock lateinit var nsslController: NotificationStackScrollLayoutController @@ -77,6 +76,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var qS: QS @JvmField @Rule val mockito = MockitoJUnit.rule() + private val configurationController = FakeConfigurationController() + @Before fun setup() { val helper = NotificationTestHelper( @@ -244,4 +245,27 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(depthController).transitionToFullShadeProgress = anyFloat() } + + @Test + fun setDragDownAmount_setsValueOnMediaHierarchyManager() { + transitionController.dragDownAmount = 10f + + verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f) + } + + @Test + fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() { + enableSplitShade() + + transitionController.dragDownAmount = 10f + + verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f) + } + + private fun enableSplitShade() { + context.getOrCreateTestableResources().addOverride( + R.bool.config_use_split_notification_shade, true + ) + configurationController.notifyConfigurationChanged() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java index 671ab597eb2a..c797bc8bdf60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java @@ -32,6 +32,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IActivityManager; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.View; @@ -228,6 +230,36 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { } @Test + public void rotationBecameAllowed_layoutParamsUpdated() { + mNotificationShadeWindowController.setKeyguardShowing(true); + when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false); + mNotificationShadeWindowController.onConfigChanged(new Configuration()); + clearInvocations(mWindowManager); + + when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true); + mNotificationShadeWindowController.onConfigChanged(new Configuration()); + + verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); + assertThat(mLayoutParameters.getValue().screenOrientation) + .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER); + } + + @Test + public void rotationBecameNotAllowed_layoutParamsUpdated() { + mNotificationShadeWindowController.setKeyguardShowing(true); + when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true); + mNotificationShadeWindowController.onConfigChanged(new Configuration()); + clearInvocations(mWindowManager); + + when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false); + mNotificationShadeWindowController.onConfigChanged(new Configuration()); + + verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); + assertThat(mLayoutParameters.getValue().screenOrientation) + .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); + } + + @Test public void batchApplyWindowLayoutParams_doesNotDispatchEvents() { mNotificationShadeWindowController.setForceDozeBrightness(true); verify(mWindowManager).updateViewLayout(any(), any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt new file mode 100644 index 000000000000..3a5d9ee16b0a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt @@ -0,0 +1,31 @@ +package com.android.systemui.statusbar.policy + +import android.content.res.Configuration + +/** Fake implementation of [ConfigurationController] for tests. */ +class FakeConfigurationController : ConfigurationController { + + private var listener: ConfigurationController.ConfigurationListener? = null + + override fun addCallback(listener: ConfigurationController.ConfigurationListener) { + this.listener = listener + } + + override fun removeCallback(listener: ConfigurationController.ConfigurationListener) { + this.listener = null + } + + override fun onConfigurationChanged(newConfiguration: Configuration?) { + listener?.onConfigChanged(newConfiguration) + } + + override fun notifyThemeChanged() { + listener?.onThemeChanged() + } + + fun notifyConfigurationChanged() { + onConfigurationChanged(newConfiguration = null) + } + + override fun isLayoutRtl(): Boolean = false +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java new file mode 100644 index 000000000000..14b9bfb1393f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java @@ -0,0 +1,203 @@ +/* + * 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 com.android.systemui.touch; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.Rect; +import android.graphics.Region; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.view.ViewRootImpl; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TouchInsetManagerTest extends SysuiTestCase { + @Mock + private View mRootView; + + @Mock + private ViewRootImpl mRootViewImpl; + + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mRootView.getViewRootImpl()).thenReturn(mRootViewImpl); + } + + @Test + public void testRootViewOnAttachedHandling() { + // Create inset manager + final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor, + mRootView); + + final ArgumentCaptor<View.OnAttachStateChangeListener> listener = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + + // Ensure manager has registered to listen to attached state of root view. + verify(mRootView).addOnAttachStateChangeListener(listener.capture()); + + // Trigger attachment and verify touchable region is set. + listener.getValue().onViewAttachedToWindow(mRootView); + verify(mRootViewImpl).setTouchableRegion(any()); + } + + @Test + public void testInsetRegionPropagation() { + // Create inset manager + final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor, + mRootView); + + // Create session + final TouchInsetManager.TouchInsetSession session = insetManager.createSession(); + + // Add a view to the session. + final Rect rect = new Rect(0, 0, 2, 2); + + session.addViewToTracking(createView(rect)); + mFakeExecutor.runAllReady(); + + // Check to see if view was properly accounted for. + final Region expectedRegion = Region.obtain(); + expectedRegion.op(rect, Region.Op.UNION); + verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion)); + } + + @Test + public void testMultipleRegions() { + // Create inset manager + final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor, + mRootView); + + // Create session + final TouchInsetManager.TouchInsetSession session = insetManager.createSession(); + + // Add a view to the session. + final Rect firstBounds = new Rect(0, 0, 2, 2); + session.addViewToTracking(createView(firstBounds)); + + mFakeExecutor.runAllReady(); + clearInvocations(mRootViewImpl); + + // Create second session + final TouchInsetManager.TouchInsetSession secondSession = insetManager.createSession(); + + // Add a view to the second session. + final Rect secondBounds = new Rect(4, 4, 8, 10); + secondSession.addViewToTracking(createView(secondBounds)); + + mFakeExecutor.runAllReady(); + + // Check to see if all views and sessions was properly accounted for. + { + final Region expectedRegion = Region.obtain(); + expectedRegion.op(firstBounds, Region.Op.UNION); + expectedRegion.op(secondBounds, Region.Op.UNION); + verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion)); + } + + + clearInvocations(mRootViewImpl); + + // clear first session, ensure second session is still reflected. + session.clear(); + mFakeExecutor.runAllReady(); + { + final Region expectedRegion = Region.obtain(); + expectedRegion.op(firstBounds, Region.Op.UNION); + verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion)); + } + } + + @Test + public void testMultipleViews() { + // Create inset manager + final TouchInsetManager insetManager = new TouchInsetManager(mFakeExecutor, + mRootView); + + // Create session + final TouchInsetManager.TouchInsetSession session = insetManager.createSession(); + + // Add a view to the session. + final Rect firstViewBounds = new Rect(0, 0, 2, 2); + session.addViewToTracking(createView(firstViewBounds)); + + // only capture second invocation. + mFakeExecutor.runAllReady(); + clearInvocations(mRootViewImpl); + + // Add a second view to the session + final Rect secondViewBounds = new Rect(4, 4, 9, 10); + final View secondView = createView(secondViewBounds); + session.addViewToTracking(secondView); + + mFakeExecutor.runAllReady(); + + // Check to see if all views and sessions was properly accounted for. + { + final Region expectedRegion = Region.obtain(); + expectedRegion.op(firstViewBounds, Region.Op.UNION); + expectedRegion.op(secondViewBounds, Region.Op.UNION); + verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion)); + } + + // Remove second view. + session.removeViewFromTracking(secondView); + + clearInvocations(mRootViewImpl); + mFakeExecutor.runAllReady(); + + // Ensure first view still reflected in touch region. + { + final Region expectedRegion = Region.obtain(); + expectedRegion.op(firstViewBounds, Region.Op.UNION); + verify(mRootViewImpl).setTouchableRegion(eq(expectedRegion)); + } + } + + private View createView(Rect bounds) { + final Rect rect = new Rect(bounds); + final View view = Mockito.mock(View.class); + doAnswer(invocation -> { + ((Rect) invocation.getArgument(0)).set(rect); + return null; + }).when(view).getBoundsOnScreen(any()); + + return view; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt new file mode 100644 index 000000000000..d4be881020e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt @@ -0,0 +1,77 @@ +/* + * 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 com.android.systemui.user + +import android.os.UserManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.LayoutInflater +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.phone.ShadeController +import com.android.systemui.statusbar.policy.UserSwitcherController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class UserSwitcherActivityTest : SysuiTestCase() { + @Mock + private lateinit var activity: UserSwitcherActivity + @Mock + private lateinit var userSwitcherController: UserSwitcherController + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var layoutInflater: LayoutInflater + @Mock + private lateinit var falsingManager: FalsingManager + @Mock + private lateinit var userManager: UserManager + @Mock + private lateinit var shadeController: ShadeController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + activity = UserSwitcherActivity( + userSwitcherController, + broadcastDispatcher, + layoutInflater, + falsingManager, + userManager, + shadeController + ) + } + + @Test + fun testMaxColumns() { + assertThat(activity.getMaxColumns(3)).isEqualTo(4) + assertThat(activity.getMaxColumns(4)).isEqualTo(4) + assertThat(activity.getMaxColumns(5)).isEqualTo(3) + assertThat(activity.getMaxColumns(6)).isEqualTo(3) + assertThat(activity.getMaxColumns(7)).isEqualTo(4) + assertThat(activity.getMaxColumns(9)).isEqualTo(5) + } +} diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java index c39b59ae35b3..ec4bfe09e415 100644 --- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java +++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java @@ -118,13 +118,14 @@ class CompanionApplicationController { serviceConnectors = CollectionUtils.map(companionServices, componentName -> new CompanionDeviceServiceConnector(mContext, userId, componentName)); - mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); - } - if (serviceConnectors.isEmpty()) { - Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: " - + packageName + ". Please check if they are correctly declared."); - return; + if (serviceConnectors.isEmpty()) { + Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: " + + packageName + ". Please check if they are correctly declared."); + return; + } + + mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); } // The first connector in the list is always the primary connector: set a listener to it. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1c10304f7223..bdb3e808b6f1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -694,12 +694,6 @@ public class ActivityManagerService extends IActivityManager.Stub return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue; } - /** - * The package name of the DeviceOwner. This package is not permitted to have its data cleared. - * <p>Not actually used</p> - */ - private volatile String mDeviceOwnerName; - private volatile int mDeviceOwnerUid = INVALID_UID; /** @@ -6238,17 +6232,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateDeviceOwner(String packageName) { - final int callingUid = Binder.getCallingUid(); - if (callingUid != 0 && callingUid != SYSTEM_UID) { - throw new SecurityException("updateDeviceOwner called from non-system process"); - } - synchronized (this) { - mDeviceOwnerName = packageName; - } - } - - @Override public void updateLockTaskPackages(int userId, String[] packages) { mActivityTaskManager.updateLockTaskPackages(userId, packages); } diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index b813bc48118a..0edbea0dbd28 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -90,6 +90,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.CompatibilityOverrideConfig; import com.android.internal.compat.IPlatformCompat; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; @@ -268,16 +269,34 @@ public final class GameManagerService extends IGameManagerService.Stub { break; } case SET_GAME_STATE: { - if (mPowerManagerInternal == null) { - final Bundle data = msg.getData(); - Slog.d(TAG, "Error setting loading mode for package " - + data.getString(PACKAGE_NAME_MSG_KEY) - + " and userId " + data.getInt(USER_ID_MSG_KEY)); - break; - } final GameState gameState = (GameState) msg.obj; final boolean isLoading = gameState.isLoading(); - mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading); + final Bundle data = msg.getData(); + final String packageName = data.getString(PACKAGE_NAME_MSG_KEY); + final int userId = data.getInt(USER_ID_MSG_KEY); + + // Restrict to games only. Requires performance mode to be enabled. + final boolean boostEnabled = + getGameMode(packageName, userId) == GameManager.GAME_MODE_PERFORMANCE; + int uid; + try { + uid = mPackageManager.getPackageUidAsUser(packageName, userId); + } catch (NameNotFoundException e) { + Slog.v(TAG, "Failed to get package metadata"); + uid = -1; + } + FrameworkStatsLog.write(FrameworkStatsLog.GAME_STATE_CHANGED, packageName, uid, + boostEnabled, gameStateModeToStatsdGameState(gameState.getMode()), + isLoading, gameState.getLabel(), gameState.getQuality()); + + if (boostEnabled) { + if (mPowerManagerInternal == null) { + Slog.d(TAG, "Error setting loading mode for package " + packageName + + " and userId " + userId); + break; + } + mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading); + } break; } } @@ -387,12 +406,6 @@ public final class GameManagerService extends IGameManagerService.Stub { // Restrict to games only. return; } - - if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) { - // Requires performance mode to be enabled. - return; - } - final Message msg = mHandler.obtainMessage(SET_GAME_STATE); final Bundle data = new Bundle(); data.putString(PACKAGE_NAME_MSG_KEY, packageName); @@ -1543,6 +1556,22 @@ public final class GameManagerService extends IGameManagerService.Stub { return out.toString(); } + private static int gameStateModeToStatsdGameState(int mode) { + switch (mode) { + case GameState.MODE_NONE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_NONE; + case GameState.MODE_GAMEPLAY_INTERRUPTIBLE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_INTERRUPTIBLE; + case GameState.MODE_GAMEPLAY_UNINTERRUPTIBLE: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_GAMEPLAY_UNINTERRUPTIBLE; + case GameState.MODE_CONTENT: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_CONTENT; + case GameState.MODE_UNKNOWN: + default: + return FrameworkStatsLog.GAME_STATE_CHANGED__STATE__MODE_UNKNOWN; + } + } + private static ServiceThread createServiceThread() { ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 76754d3e95d5..4a1a950c6a07 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -104,7 +104,7 @@ final class DreamController { pw.println(" mCurrentDream:"); pw.println(" mToken=" + mCurrentDream.mToken); pw.println(" mName=" + mCurrentDream.mName); - pw.println(" mIsTest=" + mCurrentDream.mIsTest); + pw.println(" mIsPreviewMode=" + mCurrentDream.mIsPreviewMode); pw.println(" mCanDoze=" + mCurrentDream.mCanDoze); pw.println(" mUserId=" + mCurrentDream.mUserId); pw.println(" mBound=" + mCurrentDream.mBound); @@ -117,7 +117,7 @@ final class DreamController { } public void startDream(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock, + boolean isPreviewMode, boolean canDoze, int userId, PowerManager.WakeLock wakeLock, ComponentName overlayComponentName) { stopDream(true /*immediate*/, "starting new dream"); @@ -127,10 +127,10 @@ final class DreamController { mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); Slog.i(TAG, "Starting dream: name=" + name - + ", isTest=" + isTest + ", canDoze=" + canDoze + + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId, wakeLock); + mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock); mDreamStartTime = SystemClock.elapsedRealtime(); MetricsLogger.visible(mContext, @@ -140,6 +140,7 @@ final class DreamController { intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName); + intent.putExtra(DreamService.EXTRA_IS_PREVIEW, isPreviewMode); try { if (!mContext.bindServiceAsUser(intent, mCurrentDream, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, @@ -190,7 +191,8 @@ final class DreamController { final DreamRecord oldDream = mCurrentDream; mCurrentDream = null; Slog.i(TAG, "Stopping dream: name=" + oldDream.mName - + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze + + ", isPreviewMode=" + oldDream.mIsPreviewMode + + ", canDoze=" + oldDream.mCanDoze + ", userId=" + oldDream.mUserId + ", reason='" + reason + "'" + (mSavedStopReason == null ? "" : "(from '" + mSavedStopReason + "')")); @@ -247,7 +249,7 @@ final class DreamController { mCurrentDream.mService = service; - if (!mCurrentDream.mIsTest) { + if (!mCurrentDream.mIsPreviewMode) { mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL); mCurrentDream.mSentStartBroadcast = true; } @@ -263,7 +265,7 @@ final class DreamController { private final class DreamRecord implements DeathRecipient, ServiceConnection { public final Binder mToken; public final ComponentName mName; - public final boolean mIsTest; + public final boolean mIsPreviewMode; public final boolean mCanDoze; public final int mUserId; @@ -275,11 +277,11 @@ final class DreamController { public boolean mWakingGently; - public DreamRecord(Binder token, ComponentName name, - boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { + DreamRecord(Binder token, ComponentName name, boolean isPreviewMode, + boolean canDoze, int userId, PowerManager.WakeLock wakeLock) { mToken = token; mName = name; - mIsTest = isTest; + mIsPreviewMode = isPreviewMode; mCanDoze = canDoze; mUserId = userId; mWakeLock = wakeLock; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index f0a6af3c8834..22d32a665611 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -87,7 +87,7 @@ public final class DreamManagerService extends SystemService { private Binder mCurrentDreamToken; private ComponentName mCurrentDreamName; private int mCurrentDreamUserId; - private boolean mCurrentDreamIsTest; + private boolean mCurrentDreamIsPreview; private boolean mCurrentDreamCanDoze; private boolean mCurrentDreamIsDozing; private boolean mCurrentDreamIsWaking; @@ -169,7 +169,7 @@ public final class DreamManagerService extends SystemService { pw.println("mCurrentDreamToken=" + mCurrentDreamToken); pw.println("mCurrentDreamName=" + mCurrentDreamName); pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId); - pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); + pw.println("mCurrentDreamIsPreview=" + mCurrentDreamIsPreview); pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze); pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing); pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking); @@ -190,7 +190,7 @@ public final class DreamManagerService extends SystemService { private boolean isDreamingInternal() { synchronized (mLock) { - return mCurrentDreamToken != null && !mCurrentDreamIsTest + return mCurrentDreamToken != null && !mCurrentDreamIsPreview && !mCurrentDreamIsWaking; } } @@ -235,7 +235,7 @@ public final class DreamManagerService extends SystemService { private void testDreamInternal(ComponentName dream, int userId) { synchronized (mLock) { - startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId); + startDreamLocked(dream, true /*isPreviewMode*/, false /*canDoze*/, userId); } } @@ -244,7 +244,7 @@ public final class DreamManagerService extends SystemService { final ComponentName dream = chooseDreamForUser(doze, userId); if (dream != null) { synchronized (mLock) { - startDreamLocked(dream, false /*isTest*/, doze, userId); + startDreamLocked(dream, false /*isPreviewMode*/, doze, userId); } } } @@ -395,10 +395,10 @@ public final class DreamManagerService extends SystemService { } private void startDreamLocked(final ComponentName name, - final boolean isTest, final boolean canDoze, final int userId) { + final boolean isPreviewMode, final boolean canDoze, final int userId) { if (!mCurrentDreamIsWaking && Objects.equals(mCurrentDreamName, name) - && mCurrentDreamIsTest == isTest + && mCurrentDreamIsPreview == isPreviewMode && mCurrentDreamCanDoze == canDoze && mCurrentDreamUserId == userId) { Slog.i(TAG, "Already in target dream."); @@ -412,7 +412,7 @@ public final class DreamManagerService extends SystemService { final Binder newToken = new Binder(); mCurrentDreamToken = newToken; mCurrentDreamName = name; - mCurrentDreamIsTest = isTest; + mCurrentDreamIsPreview = isPreviewMode; mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; @@ -424,7 +424,7 @@ public final class DreamManagerService extends SystemService { .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream"); mHandler.post(wakeLock.wrap(() -> { mAtmInternal.notifyDreamStateChanged(true); - mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock, + mController.startDream(newToken, name, isPreviewMode, canDoze, userId, wakeLock, mDreamOverlayServiceName); })); } @@ -457,7 +457,7 @@ public final class DreamManagerService extends SystemService { } mCurrentDreamToken = null; mCurrentDreamName = null; - mCurrentDreamIsTest = false; + mCurrentDreamIsPreview = false; mCurrentDreamCanDoze = false; mCurrentDreamUserId = 0; mCurrentDreamIsWaking = false; diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java new file mode 100644 index 000000000000..83ca16d72c9b --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java @@ -0,0 +1,64 @@ +/* + * 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 com.android.server.inputmethod; + +import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; +import static android.view.inputmethod.InputMethodManager.CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.compat.IPlatformCompat; + +/** + * A utility class used by {@link InputMethodManagerService} to manage the platform + * compatibility changes on IMF (Input Method Framework) side. + */ +final class ImePlatformCompatUtils { + private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + + /** + * Whether to finish the {@link android.view.inputmethod.InputConnection} when the device + * becomes {@link android.os.PowerManager#isInteractive non-interactive}. + * + * @param imeUid The uid of the IME application + */ + public boolean shouldFinishInputWithReportToIme(int imeUid) { + return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid); + } + + /** + * Whether to clear {@link android.view.inputmethod.InputMethodManager#SHOW_FORCED} flag + * when the next IME focused application changed. + * + * @param clientUid The uid of the app + */ + public boolean shouldClearShowForcedFlag(int clientUid) { + return isChangeEnabledByUid(CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING, clientUid); + } + + private boolean isChangeEnabledByUid(long changeFlag, int uid) { + boolean result = false; + try { + result = mPlatformCompat.isChangeEnabledByUid(changeFlag, uid); + } catch (RemoteException e) { + } + return result; + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0b7e39136feb..eb1de2a9bf91 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,7 +15,6 @@ package com.android.server.inputmethod; -import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; @@ -148,7 +147,6 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.infra.AndroidFuture; import com.android.internal.inputmethod.DirectBootAwareness; @@ -279,6 +277,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final WindowManagerInternal mWindowManagerInternal; final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; + final ImePlatformCompatUtils mImePlatformCompatUtils; final boolean mHasFeature; private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap = new ArrayMap<>(); @@ -691,8 +690,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mIsInteractive = true; - private final IPlatformCompat mPlatformCompat; - int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** @@ -1627,6 +1624,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); + mImePlatformCompatUtils = new ImePlatformCompatUtils(); mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); @@ -1634,8 +1632,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mAccessibilityManager = AccessibilityManager.getInstance(context); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); - mPlatformCompat = IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); Bundle extras = new Bundle(); @@ -3620,6 +3617,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } + final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.uid); + // In case mShowForced flag affects the next client to keep IME visible, when the current + // client is leaving due to the next focused client, we clear mShowForced flag when the + // next client's targetSdkVersion is T or higher. + if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) { + mShowForced = false; + } + // cross-profile access is always allowed here to allow profile-switching. if (!mSettings.isCurrentProfile(userId)) { Slog.w(TAG, "A background user is requesting window. Hiding IME."); @@ -4728,14 +4733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { - boolean reportToImeController = false; - try { - reportToImeController = mPlatformCompat.isChangeEnabledByUid( - FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked()); - } catch (RemoteException e) { - } scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode, - reportToImeController); + mImePlatformCompatUtils.shouldFinishInputWithReportToIme( + getCurMethodUidLocked())); } } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index a290eb3a3e2f..ea3a3d5f1c60 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -168,6 +168,7 @@ public class RuleBinaryParser implements RuleParser { switch (key) { case AtomicFormula.PACKAGE_NAME: case AtomicFormula.APP_CERTIFICATE: + case AtomicFormula.APP_CERTIFICATE_LINEAGE: case AtomicFormula.INSTALLER_NAME: case AtomicFormula.INSTALLER_CERTIFICATE: case AtomicFormula.STAMP_CERTIFICATE_HASH: diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java index 01aee7bc1942..db81393a9ad6 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java +++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.HandlerThread; import android.os.LocaleList; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -45,7 +44,6 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.content.PackageMonitor; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -89,32 +87,24 @@ class LocaleManagerBackupHelper { // SparseArray because it is more memory-efficient than a HashMap. private final SparseArray<StagedData> mStagedData; - private final PackageMonitor mPackageMonitor; private final BroadcastReceiver mUserMonitor; LocaleManagerBackupHelper(LocaleManagerService localeManagerService, - PackageManagerInternal pmInternal) { + PackageManagerInternal pmInternal, HandlerThread broadcastHandlerThread) { this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(), - new SparseArray<>()); + new SparseArray<>(), broadcastHandlerThread); } @VisibleForTesting LocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, - PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) { + PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData, + HandlerThread broadcastHandlerThread) { mContext = context; mLocaleManagerService = localeManagerService; mPackageManagerInternal = pmInternal; mClock = clock; mStagedData = stagedData; - HandlerThread broadcastHandlerThread = new HandlerThread(TAG, - Process.THREAD_PRIORITY_BACKGROUND); - broadcastHandlerThread.start(); - - mPackageMonitor = new PackageMonitorImpl(); - mPackageMonitor.register(context, broadcastHandlerThread.getLooper(), - UserHandle.ALL, - true); mUserMonitor = new UserMonitor(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); @@ -127,11 +117,6 @@ class LocaleManagerBackupHelper { return mUserMonitor; } - @VisibleForTesting - PackageMonitor getPackageMonitor() { - return mPackageMonitor; - } - /** * @see LocaleManagerInternal#getBackupPayload(int userId) */ @@ -267,6 +252,53 @@ class LocaleManagerBackupHelper { BackupManager.dataChanged(SYSTEM_BACKUP_PACKAGE_KEY); } + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageAdded} when a new package is + * added on device. + */ + void onPackageAdded(String packageName, int uid) { + try { + synchronized (mStagedDataLock) { + cleanStagedDataForOldEntriesLocked(); + + int userId = UserHandle.getUserId(uid); + if (mStagedData.contains(userId)) { + // Perform lazy restore only if the staged data exists. + doLazyRestoreLocked(packageName, userId); + } + } + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageAdded.", e); + } + } + + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageDataCleared} when a package's data + * is cleared. + */ + void onPackageDataCleared() { + try { + notifyBackupManager(); + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageDataCleared.", e); + } + } + + /** + * <p><b>Note:</b> This is invoked by service's common monitor + * {@link LocaleManagerServicePackageMonitor#onPackageRemoved} when a package is removed + * from device. + */ + void onPackageRemoved() { + try { + notifyBackupManager(); + } catch (Exception e) { + Slog.e(TAG, "Exception in onPackageRemoved.", e); + } + } + private boolean isPackageInstalledForUser(String packageName, int userId) { PackageInfo pkgInfo = null; try { @@ -395,48 +427,6 @@ class LocaleManagerBackupHelper { } /** - * Helper to monitor package states. - * - * <p>We're interested in package added, package data cleared and package removed events. - */ - private final class PackageMonitorImpl extends PackageMonitor { - @Override - public void onPackageAdded(String packageName, int uid) { - try { - synchronized (mStagedDataLock) { - cleanStagedDataForOldEntriesLocked(); - - int userId = UserHandle.getUserId(uid); - if (mStagedData.contains(userId)) { - // Perform lazy restore only if the staged data exists. - doLazyRestoreLocked(packageName, userId); - } - } - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageAdded.", e); - } - } - - @Override - public void onPackageDataCleared(String packageName, int uid) { - try { - notifyBackupManager(); - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageDataCleared.", e); - } - } - - @Override - public void onPackageRemoved(String packageName, int uid) { - try { - notifyBackupManager(); - } catch (Exception e) { - Slog.e(TAG, "Exception in onPackageRemoved.", e); - } - } - } - - /** * Performs lazy restore from the staged data. * * <p>This is invoked by the package monitor on the package added callback. diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index d459f8df99b6..c42770555bab 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.HandlerThread; import android.os.LocaleList; import android.os.Process; import android.os.RemoteException; @@ -38,14 +39,13 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.DumpUtils; +import com.android.internal.content.PackageMonitor; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; -import java.io.PrintWriter; /** * The implementation of ILocaleManager.aidl. @@ -62,6 +62,8 @@ public class LocaleManagerService extends SystemService { private LocaleManagerBackupHelper mBackupHelper; + private final PackageMonitor mPackageMonitor; + public static final boolean DEBUG = false; public LocaleManagerService(Context context) { @@ -71,15 +73,26 @@ public class LocaleManagerService extends SystemService { mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + + HandlerThread broadcastHandlerThread = new HandlerThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND); + broadcastHandlerThread.start(); + mBackupHelper = new LocaleManagerBackupHelper(this, - mPackageManagerInternal); + mPackageManagerInternal, broadcastHandlerThread); + + mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper); + mPackageMonitor.register(context, broadcastHandlerThread.getLooper(), + UserHandle.ALL, + true); } @VisibleForTesting LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal, ActivityManagerInternal activityManagerInternal, PackageManagerInternal packageManagerInternal, - LocaleManagerBackupHelper localeManagerBackupHelper) { + LocaleManagerBackupHelper localeManagerBackupHelper, + PackageMonitor packageMonitor) { super(context); mContext = context; mBinderService = new LocaleManagerBinderService(); @@ -87,6 +100,7 @@ public class LocaleManagerService extends SystemService { mActivityManagerInternal = activityManagerInternal; mPackageManagerInternal = packageManagerInternal; mBackupHelper = localeManagerBackupHelper; + mPackageMonitor = packageMonitor; } @Override @@ -130,11 +144,6 @@ public class LocaleManagerService extends SystemService { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - LocaleManagerService.this.dump(fd, pw, args); - } - - @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { @@ -407,14 +416,6 @@ public class LocaleManagerService extends SystemService { return null; } - /** - * Dumps useful info related to service. - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - // TODO(b/201766221): Implement when there is state. - } - private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED, atomRecordForMetrics.mCallingUid, diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java new file mode 100644 index 000000000000..b459be768b9f --- /dev/null +++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java @@ -0,0 +1,49 @@ +/* + * 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 com.android.server.locales; + +import com.android.internal.content.PackageMonitor; + +/** + * Helper to monitor package states inside {@link LocaleManagerService}. + * + * <p> These listeners forward the call to different aspects of locale service that + * handle the business logic. + * <p> We're interested in package added, package data cleared and package removed events. + */ +final class LocaleManagerServicePackageMonitor extends PackageMonitor { + private LocaleManagerBackupHelper mBackupHelper; + + LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) { + mBackupHelper = localeManagerBackupHelper; + } + + @Override + public void onPackageAdded(String packageName, int uid) { + mBackupHelper.onPackageAdded(packageName, uid); + } + + @Override + public void onPackageDataCleared(String packageName, int uid) { + mBackupHelper.onPackageDataCleared(); + } + + @Override + public void onPackageRemoved(String packageName, int uid) { + mBackupHelper.onPackageRemoved(); + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 16b5fb1e4d7a..a711b442c450 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -775,7 +775,7 @@ public class NotificationManagerService extends SystemService { ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages(); for (int i = 0; i < defaultDnds.size(); i++) { - allowDndPackage(defaultDnds.valueAt(i)); + allowDndPackage(userId, defaultDnds.valueAt(i)); } setDefaultAssistantForUser(userId); @@ -875,9 +875,9 @@ public class NotificationManagerService extends SystemService { } } - private void allowDndPackage(String packageName) { + private void allowDndPackage(int userId, String packageName) { try { - getBinderService().setNotificationPolicyAccessGranted(packageName, true); + getBinderService().setNotificationPolicyAccessGrantedForUser(packageName, userId, true); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 66c7c5080114..bbdea32bed59 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -478,6 +478,7 @@ public final class NotificationRecord { pw.println(prefix + "opPkg=" + getSbn().getOpPkg()); pw.println(prefix + "icon=" + notification.getSmallIcon()); pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); + pw.println(prefix + "originalFlags=0x" + Integer.toHexString(mOriginalFlags)); pw.println(prefix + "pri=" + notification.priority); pw.println(prefix + "key=" + getSbn().getKey()); pw.println(prefix + "seen=" + mStats.hasSeen()); @@ -544,6 +545,7 @@ public final class NotificationRecord { if (notification == null) { pw.println(prefix + "None"); return; + } pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent); pw.println(prefix + "contentIntent=" + notification.contentIntent); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java index c638201bf893..9bbae4bd2db5 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java @@ -29,7 +29,9 @@ import android.os.IBinder; import android.os.IHwBinder; import android.os.RemoteException; import android.system.OsConstants; +import android.util.Log; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -54,6 +56,8 @@ import java.util.concurrent.atomic.AtomicReference; * </ul> */ final class SoundTriggerHw2Compat implements ISoundTriggerHal { + private static final String TAG = "SoundTriggerHw2Compat"; + private final @NonNull Runnable mRebootRunnable; private final @NonNull IHwBinder mBinder; private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0; @@ -226,6 +230,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal { return handle.get(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); + } finally { + // TODO(b/219825762): We should be able to use the entire object in a try-with-resources + // clause, instead of having to explicitly close internal fields. + if (hidlModel.data != null) { + try { + hidlModel.data.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close file", e); + } + } } } @@ -252,6 +266,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal { return handle.get(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); + } finally { + // TODO(b/219825762): We should be able to use the entire object in a try-with-resources + // clause, instead of having to explicitly close internal fields. + if (hidlModel.common.data != null) { + try { + hidlModel.common.data.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close file", e); + } + } } } diff --git a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java index b8885e81dbe2..080a36cb2a6e 100644 --- a/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java +++ b/services/core/java/com/android/server/vibrator/StartSequentialEffectStep.java @@ -55,13 +55,15 @@ final class StartSequentialEffectStep extends Step { private long mVibratorsOnMaxDuration; + /** Start a sequential effect at the beginning. */ StartSequentialEffectStep(VibrationStepConductor conductor, CombinedVibration.Sequential effect) { this(conductor, SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0); } - StartSequentialEffectStep(VibrationStepConductor conductor, long startTime, + /** Continue a SequentialEffect from the specified index. */ + private StartSequentialEffectStep(VibrationStepConductor conductor, long startTime, CombinedVibration.Sequential effect, int index) { super(conductor, startTime); sequentialEffect = effect; @@ -123,8 +125,7 @@ final class StartSequentialEffectStep extends Step { /** * Create the next {@link StartSequentialEffectStep} to play this sequential effect, starting at - * the - * time this method is called, or null if sequence is complete. + * the time this method is called, or null if sequence is complete. */ @Nullable Step nextStep() { diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index 51691fbcdf5a..366763110565 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -16,11 +16,10 @@ package com.android.server.vibrator; -import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; import android.os.CombinedVibration; import android.os.VibrationEffect; -import android.os.WorkSource; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; @@ -40,8 +39,15 @@ import java.util.Queue; /** * Creates and manages a queue of steps for performing a VibrationEffect, as well as coordinating * dispatch of callbacks. + * + * <p>In general, methods in this class are intended to be called only by a single instance of + * VibrationThread. The only thread-safe methods for calling from other threads are the "notify" + * methods (which should never be used from the VibrationThread thread). */ final class VibrationStepConductor { + private static final boolean DEBUG = VibrationThread.DEBUG; + private static final String TAG = VibrationThread.TAG; + /** * Extra timeout added to the end of each vibration step to ensure it finishes even when * vibrator callbacks are lost. @@ -51,32 +57,24 @@ final class VibrationStepConductor { static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f; static final List<Step> EMPTY_STEP_LIST = new ArrayList<>(); - final Object mLock = new Object(); + private final Object mLock = new Object(); // Used within steps. public final VibrationSettings vibrationSettings; public final DeviceVibrationEffectAdapter deviceEffectAdapter; public final VibrationThread.VibratorManagerHooks vibratorManagerHooks; - private final WorkSource mWorkSource; private final Vibration mVibration; private final SparseArray<VibratorController> mVibrators = new SparseArray<>(); - @GuardedBy("mLock") private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>(); - @GuardedBy("mLock") private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>(); @GuardedBy("mLock") - private final Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>(); + private Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>(); - @GuardedBy("mLock") private int mPendingVibrateSteps; - @GuardedBy("mLock") - private int mConsumedStartVibrateSteps; - @GuardedBy("mLock") + private int mRemainingStartSequentialEffectSteps; private int mSuccessfulVibratorOnSteps; - @GuardedBy("mLock") - private boolean mWaitToProcessVibratorCompleteCallbacks; VibrationStepConductor(Vibration vib, VibrationSettings vibrationSettings, DeviceVibrationEffectAdapter effectAdapter, @@ -86,7 +84,6 @@ final class VibrationStepConductor { this.vibrationSettings = vibrationSettings; this.deviceEffectAdapter = effectAdapter; this.vibratorManagerHooks = vibratorManagerHooks; - this.mWorkSource = new WorkSource(mVibration.uid); CombinedVibration effect = vib.getEffect(); for (int i = 0; i < availableVibrators.size(); i++) { @@ -100,6 +97,9 @@ final class VibrationStepConductor { AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long previousStepVibratorOffTimeout) { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } if (segmentIndex >= effect.getSegments().size()) { segmentIndex = effect.getRepeatIndex(); } @@ -126,58 +126,102 @@ final class VibrationStepConductor { previousStepVibratorOffTimeout); } - public void initializeForEffect(@NonNull CombinedVibration.Sequential vibration) { - synchronized (mLock) { - mPendingVibrateSteps++; - mNextSteps.offer(new StartSequentialEffectStep(this, vibration)); + /** Called when this conductor is going to be started running by the VibrationThread. */ + public void prepareToStart() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); } + CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect()); + mPendingVibrateSteps++; + // This count is decremented at the completion of the step, so we don't subtract one. + mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size(); + mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect)); } public Vibration getVibration() { + // No thread assertion: immutable return mVibration; } - public WorkSource getWorkSource() { - return mWorkSource; - } - SparseArray<VibratorController> getVibrators() { + // No thread assertion: immutable return mVibrators; } public boolean isFinished() { - synchronized (mLock) { - return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty(); + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); } + // No need to check for vibration complete callbacks - if there were any, they would + // have no steps to notify anyway. + return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty(); } /** * Calculate the {@link Vibration.Status} based on the current queue state and the expected * number of {@link StartSequentialEffectStep} to be played. */ - public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) { - synchronized (mLock) { - if (mPendingVibrateSteps > 0 - || mConsumedStartVibrateSteps < expectedStartVibrateSteps) { - return Vibration.Status.RUNNING; - } - if (mSuccessfulVibratorOnSteps > 0) { - return Vibration.Status.FINISHED; - } - // If no step was able to turn the vibrator ON successfully. - return Vibration.Status.IGNORED_UNSUPPORTED; + public Vibration.Status calculateVibrationStatus() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + + if (mPendingVibrateSteps > 0 + || mRemainingStartSequentialEffectSteps > 0) { + return Vibration.Status.RUNNING; + } + // No pending steps, and something happened. + if (mSuccessfulVibratorOnSteps > 0) { + return Vibration.Status.FINISHED; } + // If no step was able to turn the vibrator ON successfully. + return Vibration.Status.IGNORED_UNSUPPORTED; } - /** Returns the time in millis to wait before calling {@link #runNextStep()}. */ - @GuardedBy("mLock") - public long getWaitMillisBeforeNextStepLocked() { - if (!mPendingOnVibratorCompleteSteps.isEmpty()) { - // Steps resumed by vibrator complete callback should be played right away. - return 0; + /** + * Blocks until the next step is due to run. The wait here may be interrupted by calling + * {@link #notifyWakeUp} or other "notify" methods. + * + * <p>This method returns false if the next step is ready to run now. If the method returns + * true, then some waiting was done, but may have been interrupted by a wakeUp. + * + * @return true if the method waited at all, or false if a step is ready to run now. + */ + public boolean waitUntilNextStepIsDue() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + // It's necessary to re-process callbacks if they come in after acquiring the lock to + // start waiting, but we don't want to hold the lock while processing them. + // The loop goes until there are no pending callbacks to process. + while (true) { + // TODO: cancellation checking could also be integrated here, instead of outside in + // VibrationThread. + processVibratorCompleteCallbacks(); + if (!mPendingOnVibratorCompleteSteps.isEmpty()) { + // Steps resumed by vibrator complete callback should be played right away. + return false; + } + Step nextStep = mNextSteps.peek(); + if (nextStep == null) { + return false; + } + long waitMillis = nextStep.calculateWaitTime(); + if (waitMillis <= 0) { + return false; + } + synchronized (mLock) { + // Double check for missed wake-ups before sleeping. + if (!mCompletionNotifiedVibrators.isEmpty()) { + continue; // Start again: processVibratorCompleteCallbacks will consume it. + } + try { + mLock.wait(waitMillis); + } catch (InterruptedException e) { + } + return true; + } } - Step nextStep = mNextSteps.peek(); - return nextStep == null ? 0 : nextStep.calculateWaitTime(); } /** @@ -185,72 +229,88 @@ final class VibrationStepConductor { * to be played next. */ public void runNextStep() { - // Vibrator callbacks should wait until the polled step is played and the next steps are - // added back to the queue, so they can handle the callback. - markWaitToProcessVibratorCallbacks(); - try { - Step nextStep = pollNext(); - if (nextStep != null) { - // This might turn on the vibrator and have a HAL latency. Execute this outside - // any lock to avoid blocking other interactions with the thread. - List<Step> nextSteps = nextStep.play(); - synchronized (mLock) { - if (nextStep.getVibratorOnDuration() > 0) { - mSuccessfulVibratorOnSteps++; - } - if (nextStep instanceof StartSequentialEffectStep) { - mConsumedStartVibrateSteps++; - } - if (!nextStep.isCleanUp()) { - mPendingVibrateSteps--; - } - for (int i = 0; i < nextSteps.size(); i++) { - mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1; - } - mNextSteps.addAll(nextSteps); - } + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + // In theory a completion callback could have come in between the wait finishing and + // this method starting, but that only means the step is due now anyway, so it's reasonable + // to run it before processing callbacks as the window is tiny. + Step nextStep = pollNext(); + if (nextStep != null) { + List<Step> nextSteps = nextStep.play(); + if (nextStep.getVibratorOnDuration() > 0) { + mSuccessfulVibratorOnSteps++; } - } finally { - synchronized (mLock) { - processVibratorCompleteCallbacksLocked(); + if (nextStep instanceof StartSequentialEffectStep) { + mRemainingStartSequentialEffectSteps--; } + if (!nextStep.isCleanUp()) { + mPendingVibrateSteps--; + } + for (int i = 0; i < nextSteps.size(); i++) { + mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1; + } + mNextSteps.addAll(nextSteps); } } /** - * Notify the vibrator completion. + * Wake up the execution thread, which may be waiting until the next step is due. + * The caller is responsible for diverting VibrationThread execution. * - * <p>This is a lightweight method that do not trigger any operation from {@link - * VibratorController}, so it can be called directly from a native callback. + * <p>At the moment this is used after the signal is set that a cancellation needs to be + * processed. The actual cancellation will be invoked from the VibrationThread. */ - @GuardedBy("mLock") - private void notifyVibratorCompleteLocked(int vibratorId) { - mCompletionNotifiedVibrators.offer(vibratorId); - if (!mWaitToProcessVibratorCompleteCallbacks) { - // No step is being played or cancelled now, process the callback right away. - processVibratorCompleteCallbacksLocked(); + public void notifyWakeUp() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(false); + } + + synchronized (mLock) { + mLock.notify(); } } + /** + * Notify the conductor that a vibrator has completed its work. + * + * <p>This is a lightweight method intended to be called directly via native callbacks. + * The state update is recorded for processing on the main execution thread (VibrationThread). + */ public void notifyVibratorComplete(int vibratorId) { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(false); + } + + if (DEBUG) { + Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId); + } + synchronized (mLock) { - if (VibrationThread.DEBUG) { - Slog.d(VibrationThread.TAG, - "Vibration complete reported by vibrator " + vibratorId); - } - notifyVibratorCompleteLocked(vibratorId); + mCompletionNotifiedVibrators.offer(vibratorId); mLock.notify(); } } + /** + * Notify that a VibratorManager sync operation has completed. + * + * <p>This is a lightweight method intended to be called directly via native callbacks. + * The state update is recorded for processing on the main execution thread + * (VibrationThread). + */ public void notifySyncedVibrationComplete() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(false); + } + + if (DEBUG) { + Slog.d(TAG, "Synced vibration complete reported by vibrator manager"); + } + synchronized (mLock) { - if (VibrationThread.DEBUG) { - Slog.d(VibrationThread.TAG, - "Synced vibration complete reported by vibrator manager"); - } for (int i = 0; i < mVibrators.size(); i++) { - notifyVibratorCompleteLocked(mVibrators.keyAt(i)); + mCompletionNotifiedVibrators.offer(mVibrators.keyAt(i)); } mLock.notify(); } @@ -263,25 +323,20 @@ final class VibrationStepConductor { * {@link Step#cancel()}. */ public void cancel() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + // Vibrator callbacks should wait until all steps from the queue are properly cancelled // and clean up steps are added back to the queue, so they can handle the callback. - markWaitToProcessVibratorCallbacks(); - try { - List<Step> cleanUpSteps = new ArrayList<>(); - Step step; - while ((step = pollNext()) != null) { - cleanUpSteps.addAll(step.cancel()); - } - synchronized (mLock) { - // All steps generated by Step.cancel() should be clean-up steps. - mPendingVibrateSteps = 0; - mNextSteps.addAll(cleanUpSteps); - } - } finally { - synchronized (mLock) { - processVibratorCompleteCallbacksLocked(); - } + List<Step> cleanUpSteps = new ArrayList<>(); + Step step; + while ((step = pollNext()) != null) { + cleanUpSteps.addAll(step.cancel()); } + // All steps generated by Step.cancel() should be clean-up steps. + mPendingVibrateSteps = 0; + mNextSteps.addAll(cleanUpSteps); } /** @@ -290,57 +345,55 @@ final class VibrationStepConductor { * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order. */ public void cancelImmediately() { - // Vibrator callbacks should wait until all steps from the queue are properly cancelled. - markWaitToProcessVibratorCallbacks(); - try { - Step step; - while ((step = pollNext()) != null) { - // This might turn off the vibrator and have a HAL latency. Execute this outside - // any lock to avoid blocking other interactions with the thread. - step.cancelImmediately(); - } - synchronized (mLock) { - mPendingVibrateSteps = 0; - } - } finally { - synchronized (mLock) { - processVibratorCompleteCallbacksLocked(); - } + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + + Step step; + while ((step = pollNext()) != null) { + step.cancelImmediately(); } + mPendingVibrateSteps = 0; } @Nullable private Step pollNext() { - synchronized (mLock) { - // Prioritize the steps resumed by a vibrator complete callback. - if (!mPendingOnVibratorCompleteSteps.isEmpty()) { - return mPendingOnVibratorCompleteSteps.poll(); - } - return mNextSteps.poll(); + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); } - } - private void markWaitToProcessVibratorCallbacks() { - synchronized (mLock) { - mWaitToProcessVibratorCompleteCallbacks = true; + // Prioritize the steps resumed by a vibrator complete callback, irrespective of their + // "next run time". + if (!mPendingOnVibratorCompleteSteps.isEmpty()) { + return mPendingOnVibratorCompleteSteps.poll(); } + return mNextSteps.poll(); } /** - * Notify the step in this queue that should be resumed by the vibrator completion - * callback and keep it separate to be consumed by {@link #runNextStep()}. - * - * <p>This is a lightweight method that do not trigger any operation from {@link - * VibratorController}, so it can be called directly from a native callback. + * Process any notified vibrator completions. * * <p>This assumes only one of the next steps is waiting on this given vibrator, so the * first step found will be resumed by this method, in no particular order. */ - @GuardedBy("mLock") - private void processVibratorCompleteCallbacksLocked() { - mWaitToProcessVibratorCompleteCallbacks = false; - while (!mCompletionNotifiedVibrators.isEmpty()) { - int vibratorId = mCompletionNotifiedVibrators.poll(); + private void processVibratorCompleteCallbacks() { + if (Build.IS_DEBUGGABLE) { + expectIsVibrationThread(true); + } + + Queue<Integer> vibratorsToProcess; + // Swap out the queue of completions to process. + synchronized (mLock) { + if (mCompletionNotifiedVibrators.isEmpty()) { + return; // Nothing to do. + } + + vibratorsToProcess = mCompletionNotifiedVibrators; + mCompletionNotifiedVibrators = new LinkedList<>(); + } + + while (!vibratorsToProcess.isEmpty()) { + int vibratorId = vibratorsToProcess.poll(); Iterator<Step> it = mNextSteps.iterator(); while (it.hasNext()) { Step step = it.next(); @@ -352,4 +405,27 @@ final class VibrationStepConductor { } } } + + private static CombinedVibration.Sequential toSequential(CombinedVibration effect) { + if (effect instanceof CombinedVibration.Sequential) { + return (CombinedVibration.Sequential) effect; + } + return (CombinedVibration.Sequential) CombinedVibration.startSequential() + .addNext(effect) + .combine(); + } + + /** + * This check is used for debugging and documentation to indicate the thread that's expected + * to invoke a given public method on this class. Most methods are only invoked by + * VibrationThread, which is where all the steps and HAL calls should be made. Other threads + * should only signal to the execution flow being run by VibrationThread. + */ + private static void expectIsVibrationThread(boolean isVibrationThread) { + if ((Thread.currentThread() instanceof VibrationThread) != isVibrationThread) { + Slog.wtfStack("VibrationStepConductor", + "Thread caller assertion failed, expected isVibrationThread=" + + isVibrationThread); + } + } } diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index f2cd8c3ec3f8..3fef7f2a747e 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -16,17 +16,15 @@ package com.android.server.vibrator; -import android.os.CombinedVibration; import android.os.IBinder; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.Trace; +import android.os.WorkSource; import android.util.Slog; import android.util.SparseArray; -import com.android.internal.annotations.VisibleForTesting; - import java.util.NoSuchElementException; /** Plays a {@link Vibration} in dedicated thread. */ @@ -105,11 +103,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return mStepConductor.getVibration(); } - @VisibleForTesting - SparseArray<VibratorController> getVibrators() { - return mStepConductor.getVibrators(); - } - @Override public void binderDied() { if (DEBUG) { @@ -136,12 +129,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */ private void runWithWakeLock() { - mWakeLock.setWorkSource(mStepConductor.getWorkSource()); + WorkSource workSource = new WorkSource(mStepConductor.getVibration().uid); + mWakeLock.setWorkSource(workSource); mWakeLock.acquire(); try { runWithWakeLockAndDeathLink(); } finally { mWakeLock.release(); + mWakeLock.setWorkSource(null); } } @@ -178,12 +173,10 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return; } mStop = true; - synchronized (mStepConductor.mLock) { - if (DEBUG) { - Slog.d(TAG, "Vibration cancelled"); - } - mStepConductor.mLock.notify(); + if (DEBUG) { + Slog.d(TAG, "Vibration cancelled"); } + mStepConductor.notifyWakeUp(); } /** Cancel current vibration and shuts off the vibrators immediately. */ @@ -192,13 +185,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { // Already forced the thread to stop, wait for it to finish. return; } - mStop = mForceStop = true; - synchronized (mStepConductor.mLock) { - if (DEBUG) { - Slog.d(TAG, "Vibration cancelled immediately"); - } - mStepConductor.mLock.notify(); + if (DEBUG) { + Slog.d(TAG, "Vibration cancelled immediately"); } + mStop = mForceStop = true; + mStepConductor.notifyWakeUp(); } /** Notify current vibration that a synced step has completed. */ @@ -225,27 +216,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { private void playVibration() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration"); try { - CombinedVibration.Sequential sequentialEffect = - toSequential(mStepConductor.getVibration().getEffect()); - final int sequentialEffectSize = sequentialEffect.getEffects().size(); - mStepConductor.initializeForEffect(sequentialEffect); + mStepConductor.prepareToStart(); while (!mStepConductor.isFinished()) { - long waitMillisBeforeNextStep; - synchronized (mStepConductor.mLock) { - waitMillisBeforeNextStep = mStepConductor.getWaitMillisBeforeNextStepLocked(); - if (waitMillisBeforeNextStep > 0) { - try { - mStepConductor.mLock.wait(waitMillisBeforeNextStep); - } catch (InterruptedException e) { - } - } - } - // Only run the next vibration step if we didn't have to wait in this loop. - // If we waited then the queue may have changed or the wait could have been - // interrupted by a cancel call, so loop again to re-evaluate the scheduling of - // the queue top element. - if (waitMillisBeforeNextStep <= 0) { + // Skip wait and next step if mForceStop already happened. + boolean waited = mForceStop || mStepConductor.waitUntilNextStepIsDue(); + // If we waited, don't run the next step, but instead re-evaluate cancellation + // status + if (!waited) { if (DEBUG) { Slog.d(TAG, "Play vibration consuming next step..."); } @@ -253,8 +231,17 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { // blocking the thread. mStepConductor.runNextStep(); } + + if (mForceStop) { + // Cancel every step and stop playing them right away, even clean-up steps. + mStepConductor.cancelImmediately(); + clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED); + break; + } + Vibration.Status status = mStop ? Vibration.Status.CANCELLED - : mStepConductor.calculateVibrationStatus(sequentialEffectSize); + : mStepConductor.calculateVibrationStatus(); + // This block can only run once due to mCalledVibrationCompleteCallback. if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) { // First time vibration stopped running, start clean-up tasks and notify // callback immediately. @@ -263,25 +250,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { mStepConductor.cancel(); } } - if (mForceStop) { - // Cancel every step and stop playing them right away, even clean-up steps. - mStepConductor.cancelImmediately(); - clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED); - break; - } } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } } - - private static CombinedVibration.Sequential toSequential(CombinedVibration effect) { - if (effect instanceof CombinedVibration.Sequential) { - return (CombinedVibration.Sequential) effect; - } - return (CombinedVibration.Sequential) CombinedVibration.startSequential() - .addNext(effect) - .combine(); - } - } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b0efa5b283bb..d7725865f609 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -148,6 +148,7 @@ import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN; import static com.android.server.wm.ActivityRecordProto.APP_STOPPED; import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE; import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT; +import static com.android.server.wm.ActivityRecordProto.ENABLE_RECENTS_SCREENSHOT; import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT; import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK; import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE; @@ -771,7 +772,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Last visibility state we reported to the app token. boolean reportedVisible; - boolean mEnablePreviewScreenshots = true; + boolean mEnableRecentsScreenshot = true; // Information about an application starting window if displayed. // Note: these are de-referenced before the starting window animates away. @@ -5151,7 +5152,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * See {@link Activity#setRecentsScreenshotEnabled}. */ void setRecentsScreenshotEnabled(boolean enabled) { - mEnablePreviewScreenshots = enabled; + mEnableRecentsScreenshot = enabled; } /** @@ -5163,7 +5164,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * screenshot. */ boolean shouldUseAppThemeSnapshot() { - return !mEnablePreviewScreenshots || forAllWindows(WindowState::isSecureLocked, + return !mEnableRecentsScreenshot || forAllWindows(WindowState::isSecureLocked, true /* topToBottom */); } @@ -9184,6 +9185,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Only record if max bounds sandboxing is applied, if the caller has the necessary // permission to access the device configs. proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds()); + proto.write(ENABLE_RECENTS_SCREENSHOT, mEnableRecentsScreenshot); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4c5c7054e9a0..fd24565798fb 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5499,7 +5499,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * display, which set unrestricted keep-clear areas. * * For context on restricted vs unrestricted keep-clear areas, see - * {@link android.Manifest.permission.USE_UNRESTRICTED_KEEP_CLEAR_AREAS}. + * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS}. */ void getKeepClearAreas(List<Rect> outRestricted, List<Rect> outUnrestricted) { final Matrix tmpMatrix = new Matrix(); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c267cbacdf24..c13ae95217b5 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IApplicationThread; +import android.app.WindowConfiguration; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; @@ -296,6 +297,17 @@ class TransitionController { return ci.mVisible; } + @WindowConfiguration.WindowingMode + int getWindowingModeAtStart(@NonNull WindowContainer wc) { + if (mCollectingTransition == null) return wc.getWindowingMode(); + final Transition.ChangeInfo ci = mCollectingTransition.mChanges.get(wc); + if (ci == null) { + // not part of transition, so use current state. + return wc.getWindowingMode(); + } + return ci.mWindowingMode; + } + @WindowManager.TransitionType int getCollectingTransitionType() { return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE; diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 24493e2541e1..14737d44fe22 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -196,8 +196,11 @@ class WallpaperController { "Win " + w + ": token animating, looking behind."); } mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground()); - // Found a target! End search. - return true; + // While the keyguard is going away, both notification shade and a normal activity such + // as a launcher can satisfy criteria for a wallpaper target. In this case, we should + // chose the normal activity, otherwise wallpaper becomes invisible when a new animation + // starts before the keyguard going away animation finishes. + return w.mActivityRecord != null; } return false; }; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5b1021eefc0a..709f885db776 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -200,6 +200,7 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteCallback; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -288,6 +289,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IKeyguardLockedStateListener; import com.android.internal.policy.IShortcutService; import com.android.internal.policy.KeyInterceptionInfo; import com.android.internal.protolog.ProtoLogImpl; @@ -460,6 +462,10 @@ public class WindowManagerService extends IWindowManager.Stub final private KeyguardDisableHandler mKeyguardDisableHandler; + private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners = + new RemoteCallbackList<>(); + private boolean mDispatchedKeyguardLockedState = false; + // VR Vr2d Display Id. int mVr2dDisplayId = INVALID_DISPLAY; boolean mVrModeEnabled = false; @@ -3029,6 +3035,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void onKeyguardShowingAndNotOccludedChanged() { mH.sendEmptyMessage(H.RECOMPUTE_FOCUS); + dispatchKeyguardLockedStateState(); } @Override @@ -3218,6 +3225,50 @@ public class WindowManagerService extends IWindowManager.Stub } } + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + boolean registered = mKeyguardLockedStateListeners.register(listener); + if (!registered) { + Slog.w(TAG, "Failed to register listener: " + listener); + } + } + + @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) + @Override + public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) { + enforceSubscribeToKeyguardLockedStatePermission(); + mKeyguardLockedStateListeners.unregister(listener); + } + + private void enforceSubscribeToKeyguardLockedStatePermission() { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE, + Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE + + " permission required to read keyguard visibility"); + } + + private void dispatchKeyguardLockedStateState() { + mH.post(() -> { + final boolean isKeyguardLocked = mPolicy.isKeyguardShowing(); + if (mDispatchedKeyguardLockedState == isKeyguardLocked) { + return; + } + final int n = mKeyguardLockedStateListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged( + isKeyguardLocked); + } catch (RemoteException e) { + // Handled by the RemoteCallbackList. + } + } + mKeyguardLockedStateListeners.finishBroadcast(); + mDispatchedKeyguardLockedState = isKeyguardLocked; + }); + } + @Override public void setSwitchingUser(boolean switching) { if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index ccaa03ae8fd0..a2e8813c998a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; @@ -645,8 +646,10 @@ class WindowToken extends WindowContainer<WindowState> { final ActivityRecord r = asActivityRecord(); if (r != null) { final Task rootTask = r.getRootTask(); - // Don't transform the activity in PiP because the PiP task organizer will handle it. - if (rootTask != null && rootTask.inPinnedWindowingMode()) { + // Don't transform the activity exiting PiP because the PiP task organizer will handle + // it. + if (rootTask != null && mTransitionController.getWindowingModeAtStart(rootTask) + == WINDOWING_MODE_PINNED) { return; } } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index a9c6b8d67cac..11714dc821af 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -50,6 +50,7 @@ #include "android_runtime/Log.h" #include "gnss/AGnss.h" #include "gnss/AGnssRil.h" +#include "gnss/Gnss.h" #include "gnss/GnssAntennaInfo.h" #include "gnss/GnssAntennaInfoCallback.h" #include "gnss/GnssBatching.h" @@ -68,18 +69,7 @@ static jclass class_gnssPowerStats; -static jmethodID method_reportLocation; -static jmethodID method_reportStatus; -static jmethodID method_reportSvStatus; -static jmethodID method_reportNmea; -static jmethodID method_setTopHalCapabilities; -static jmethodID method_setGnssYearOfHardware; -static jmethodID method_setGnssHardwareModelName; -static jmethodID method_psdsDownloadRequest; static jmethodID method_reportNiNotification; -static jmethodID method_requestLocation; -static jmethodID method_requestUtcTime; -static jmethodID method_reportGnssServiceDied; static jmethodID method_reportGnssPowerStats; static jmethodID method_reportNfwNotification; static jmethodID method_isInEmergencySession; @@ -92,7 +82,6 @@ using android::status_t; using android::String16; using android::wp; using android::binder::Status; -using android::gnss::GnssConfigurationInterface; using android::hardware::Return; using android::hardware::Void; @@ -128,12 +117,8 @@ using android::hardware::gnss::GnssConstellationType; using android::hardware::gnss::GnssPowerStats; using android::hardware::gnss::IGnssPowerIndication; using android::hardware::gnss::IGnssPowerIndicationCallback; -using android::hardware::gnss::PsdsType; -using IAGnssAidl = android::hardware::gnss::IAGnss; -using IAGnssRilAidl = android::hardware::gnss::IAGnssRil; using IGnssAidl = android::hardware::gnss::IGnss; -using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback; using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching; using IGnssDebugAidl = android::hardware::gnss::IGnssDebug; using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds; @@ -142,575 +127,30 @@ using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration; using GnssLocationAidl = android::hardware::gnss::GnssLocation; using IGnssAntennaInfoAidl = android::hardware::gnss::IGnssAntennaInfo; -struct GnssDeathRecipient : virtual public hidl_death_recipient -{ - // hidl_death_recipient interface - virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override { - ALOGE("IGNSS hidl service failed, trying to recover..."); - - JNIEnv* env = android::AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied); - } -}; - -// Must match the value from GnssMeasurement.java -static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4); - -sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr; -sp<IGnss_V1_0> gnssHal = nullptr; -sp<IGnss_V1_1> gnssHal_V1_1 = nullptr; -sp<IGnss_V2_0> gnssHal_V2_0 = nullptr; -sp<IGnss_V2_1> gnssHal_V2_1 = nullptr; -sp<IGnssAidl> gnssHalAidl = nullptr; -sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr; -sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr; -sp<IGnssXtra> gnssXtraIface = nullptr; sp<IGnssNi> gnssNiIface = nullptr; sp<IGnssPowerIndication> gnssPowerIndicationIface = nullptr; -std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr; +std::unique_ptr<android::gnss::GnssHal> gnssHal = nullptr; +std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr; +std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr; +std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr; +std::unique_ptr<android::gnss::GnssConfigurationInterface> gnssConfigurationIface = nullptr; std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr; std::unique_ptr<android::gnss::GnssNavigationMessageInterface> gnssNavigationMessageIface = nullptr; std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr; -std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr; -std::unique_ptr<android::gnss::AGnssInterface> agnssIface = nullptr; std::unique_ptr<android::gnss::GnssDebugInterface> gnssDebugIface = nullptr; -std::unique_ptr<android::gnss::AGnssRilInterface> agnssRilIface = nullptr; +std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr; +std::unique_ptr<android::gnss::GnssPsdsInterface> gnssPsdsIface = nullptr; std::unique_ptr<android::gnss::GnssVisibilityControlInterface> gnssVisibilityControlIface = nullptr; -std::unique_ptr<android::gnss::GnssAntennaInfoInterface> gnssAntennaInfoIface = nullptr; std::unique_ptr<android::gnss::MeasurementCorrectionsInterface> gnssMeasurementCorrectionsIface = nullptr; -#define WAKE_LOCK_NAME "GPS" - namespace android { namespace { -// Returns true if location has lat/long information. -bool hasLatLong(const GnssLocationAidl& location) { - return (location.gnssLocationFlags & GnssLocationAidl::HAS_LAT_LONG) != 0; -} - -// Returns true if location has lat/long information. -bool hasLatLong(const GnssLocation_V1_0& location) { - return (static_cast<uint32_t>(location.gnssLocationFlags) & - GnssLocationFlags::HAS_LAT_LONG) != 0; -} - -// Returns true if location has lat/long information. -bool hasLatLong(const GnssLocation_V2_0& location) { - return hasLatLong(location.v1_0); -} - -bool isSvStatusRegistered = false; -bool isNmeaRegistered = false; - } // namespace -static inline jboolean boolToJbool(bool value) { - return value ? JNI_TRUE : JNI_FALSE; -} - -static GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees, - jdouble longitudeDegrees, jdouble altitudeMeters, - jfloat speedMetersPerSec, jfloat bearingDegrees, - jfloat horizontalAccuracyMeters, - jfloat verticalAccuracyMeters, - jfloat speedAccuracyMetersPerSecond, - jfloat bearingAccuracyDegrees, jlong timestamp, - jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, - jdouble elapsedRealtimeUncertaintyNanos) { - GnssLocationAidl location; - location.gnssLocationFlags = static_cast<int>(gnssLocationFlags); - location.latitudeDegrees = static_cast<double>(latitudeDegrees); - location.longitudeDegrees = static_cast<double>(longitudeDegrees); - location.altitudeMeters = static_cast<double>(altitudeMeters); - location.speedMetersPerSec = static_cast<double>(speedMetersPerSec); - location.bearingDegrees = static_cast<double>(bearingDegrees); - location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters); - location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters); - location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond); - location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees); - location.timestampMillis = static_cast<uint64_t>(timestamp); - - location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags); - location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos); - location.elapsedRealtime.timeUncertaintyNs = - static_cast<double>(elapsedRealtimeUncertaintyNanos); - - return location; -} - -static GnssLocation_V1_0 createGnssLocation_V1_0( - jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees, - jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees, - jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, - jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, - jlong timestamp) { - GnssLocation_V1_0 location; - location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags); - location.latitudeDegrees = static_cast<double>(latitudeDegrees); - location.longitudeDegrees = static_cast<double>(longitudeDegrees); - location.altitudeMeters = static_cast<double>(altitudeMeters); - location.speedMetersPerSec = static_cast<float>(speedMetersPerSec); - location.bearingDegrees = static_cast<float>(bearingDegrees); - location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters); - location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters); - location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond); - location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees); - location.timestamp = static_cast<uint64_t>(timestamp); - - return location; -} - -static GnssLocation_V2_0 createGnssLocation_V2_0( - jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees, - jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees, - jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, - jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, - jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, - jdouble elapsedRealtimeUncertaintyNanos) { - GnssLocation_V2_0 location; - location.v1_0 = createGnssLocation_V1_0( - gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters, - speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters, - verticalAccuracyMeters, speedAccuracyMetersPerSecond, - bearingAccuracyDegrees, timestamp); - - location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags); - location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos); - location.elapsedRealtime.timeUncertaintyNs = static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos); - - return location; -} - -/* - * GnssCallback class implements the callback methods for IGnss interface. - */ -struct GnssCallback : public IGnssCallback_V2_1 { - Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override; - Return<void> gnssStatusCb(const IGnssCallback_V1_0::GnssStatusValue status) override; - Return<void> gnssSvStatusCb(const IGnssCallback_V1_0::GnssSvStatus& svStatus) override { - return gnssSvStatusCbImpl<IGnssCallback_V1_0::GnssSvStatus, IGnssCallback_V1_0::GnssSvInfo>( - svStatus); - } - Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override; - Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override; - Return<void> gnssAcquireWakelockCb() override; - Return<void> gnssReleaseWakelockCb() override; - Return<void> gnssRequestTimeCb() override; - Return<void> gnssRequestLocationCb(const bool independentFromGnss) override; - - Return<void> gnssSetSystemInfoCb(const IGnssCallback_V1_0::GnssSystemInfo& info) override; - - // New in 1.1 - Return<void> gnssNameCb(const android::hardware::hidl_string& name) override; - - // New in 2.0 - Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool isUserEmergency) - override; - Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override; - Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override; - Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override { - return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_0::GnssSvInfo>, - IGnssCallback_V1_0::GnssSvInfo>(svInfoList); - } - - // New in 2.1 - Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override { - return gnssSvStatusCbImpl<hidl_vec<IGnssCallback_V2_1::GnssSvInfo>, - IGnssCallback_V1_0::GnssSvInfo>(svInfoList); - } - Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override; - - // TODO: Reconsider allocation cost vs threadsafety on these statics - static const char* sNmeaString; - static size_t sNmeaStringLength; - - template <class T> - static Return<void> gnssLocationCbImpl(const T& location); - - template <class T_list, class T_sv_info> - static Return<void> gnssSvStatusCbImpl(const T_list& svStatus); - -private: - template <class T> - static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) { - return 0; - } - - template <class T> - static double getBasebandCn0DbHz(const T& svStatus, size_t i) { - return 0.0; - } - - template <class T> - static uint32_t getGnssSvInfoListSize(const T& svInfoList) { - return svInfoList.size(); - } - - static const IGnssCallbackAidl::GnssSvInfo& getGnssSvInfoOfIndex( - const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) { - return svInfoList[i]; - } - - static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex( - const IGnssCallback_V1_0::GnssSvStatus& svStatus, size_t i) { - return svStatus.gnssSvList.data()[i]; - } - - static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex( - const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList, size_t i) { - return svInfoList[i].v1_0; - } - - static const IGnssCallback_V1_0::GnssSvInfo& getGnssSvInfoOfIndex( - const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { - return svInfoList[i].v2_0.v1_0; - } - - template <class T> - static uint32_t getConstellationType(const T& svInfoList, size_t i) { - return static_cast<uint32_t>(svInfoList[i].constellation); - } -}; - -Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) { - ALOGD("%s: name=%s\n", __func__, name.c_str()); - - JNIEnv* env = getJniEnv(); - jstring jstringName = env->NewStringUTF(name.c_str()); - env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); - if (jstringName) { - env->DeleteLocalRef(jstringName); - } - checkAndClearExceptionFromCallback(env, __FUNCTION__); - - return Void(); -} - -const char* GnssCallback::sNmeaString = nullptr; -size_t GnssCallback::sNmeaStringLength = 0; - -template<class T> -Return<void> GnssCallback::gnssLocationCbImpl(const T& location) { - JNIEnv* env = getJniEnv(); - - jobject jLocation = translateGnssLocation(env, location); - - env->CallVoidMethod(mCallbacksObj, - method_reportLocation, - boolToJbool(hasLatLong(location)), - jLocation); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - env->DeleteLocalRef(jLocation); - return Void(); -} - -Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) { - return gnssLocationCbImpl<GnssLocation_V1_0>(location); -} - -Return<void> -GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) { - return gnssLocationCbImpl<GnssLocation_V2_0>(location); -} - -Return<void> GnssCallback::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -template<> -uint32_t GnssCallback::getHasBasebandCn0DbHzFlag(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& - svStatus) { - return SVID_FLAGS_HAS_BASEBAND_CN0; -} - -template <> -uint32_t GnssCallback::getHasBasebandCn0DbHzFlag( - const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) { - return SVID_FLAGS_HAS_BASEBAND_CN0; -} - -template <> -double GnssCallback::getBasebandCn0DbHz( - const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) { - return svInfoList[i].basebandCN0DbHz; -} - -template<> -double GnssCallback::getBasebandCn0DbHz(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, - size_t i) { - return svInfoList[i].basebandCN0DbHz; -} - -template <> -uint32_t GnssCallback::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) { - return svStatus.numSvs; -} - -template <> -uint32_t GnssCallback::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, - size_t i) { - return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation); -} - -template <> -uint32_t GnssCallback::getConstellationType( - const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { - return static_cast<uint32_t>(svInfoList[i].v2_0.constellation); -} - -template <class T_list, class T_sv_info> -Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) { - // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework. - if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() <= 1) { - if (!isSvStatusRegistered) { - return Void(); - } - } - - JNIEnv* env = getJniEnv(); - - uint32_t listSize = getGnssSvInfoListSize(svStatus); - - jintArray svidWithFlagArray = env->NewIntArray(listSize); - jfloatArray cn0Array = env->NewFloatArray(listSize); - jfloatArray elevArray = env->NewFloatArray(listSize); - jfloatArray azimArray = env->NewFloatArray(listSize); - jfloatArray carrierFreqArray = env->NewFloatArray(listSize); - jfloatArray basebandCn0Array = env->NewFloatArray(listSize); - - jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0); - jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0); - jfloat* elev = env->GetFloatArrayElements(elevArray, 0); - jfloat* azim = env->GetFloatArrayElements(azimArray, 0); - jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0); - jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0); - - /* - * Read GNSS SV info. - */ - for (size_t i = 0; i < listSize; ++i) { - enum ShiftWidth: uint8_t { - SVID_SHIFT_WIDTH = 12, - CONSTELLATION_TYPE_SHIFT_WIDTH = 8 - }; - - const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i); - svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) | - (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) | - static_cast<uint32_t>(info.svFlag); - cn0s[i] = info.cN0Dbhz; - elev[i] = info.elevationDegrees; - azim[i] = info.azimuthDegrees; - carrierFreq[i] = info.carrierFrequencyHz; - svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus); - basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i); - } - - env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0); - env->ReleaseFloatArrayElements(cn0Array, cn0s, 0); - env->ReleaseFloatArrayElements(elevArray, elev, 0); - env->ReleaseFloatArrayElements(azimArray, azim, 0); - env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0); - env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0); - - env->CallVoidMethod(mCallbacksObj, method_reportSvStatus, - static_cast<jint>(listSize), svidWithFlagArray, cn0Array, elevArray, azimArray, - carrierFreqArray, basebandCn0Array); - - env->DeleteLocalRef(svidWithFlagArray); - env->DeleteLocalRef(cn0Array); - env->DeleteLocalRef(elevArray); - env->DeleteLocalRef(azimArray); - env->DeleteLocalRef(carrierFreqArray); - env->DeleteLocalRef(basebandCn0Array); - - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp, - const ::android::hardware::hidl_string& nmea) { - // In HIDL, if no listener is registered, do not report nmea to the framework. - if (!isNmeaRegistered) { - return Void(); - } - JNIEnv* env = getJniEnv(); - /* - * The Java code will call back to read these values. - * We do this to avoid creating unnecessary String objects. - */ - sNmeaString = nmea.c_str(); - sNmeaStringLength = nmea.size(); - - env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) { - ALOGD("%s: %du\n", __func__, capabilities); - - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -Return<void> GnssCallback::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) { - return GnssCallback::gnssSetCapabilitesCb(capabilities); -} - -Return<void> GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) { - return GnssCallback::gnssSetCapabilitesCb(capabilities); -} - -Return<void> GnssCallback::gnssAcquireWakelockCb() { - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); - return Void(); -} - -Return<void> GnssCallback::gnssReleaseWakelockCb() { - release_wake_lock(WAKE_LOCK_NAME); - return Void(); -} - -Return<void> GnssCallback::gnssRequestTimeCb() { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) { - return GnssCallback::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */ - false); -} - -Return<void> GnssCallback::gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool - isUserEmergency) { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), - boolToJbool(isUserEmergency)); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) { - ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw); - - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, - info.yearOfHw); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback { -public: - Status gnssSetCapabilitiesCb(const int capabilities) override; - Status gnssStatusCb(const GnssStatusValue status) override; - Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override; - Status gnssLocationCb(const GnssLocationAidl& location) override; - Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override; - Status gnssAcquireWakelockCb() override; - Status gnssReleaseWakelockCb() override; - Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override; - Status gnssRequestTimeCb() override; - Status gnssRequestLocationCb(const bool independentFromGnss, - const bool isUserEmergency) override; -}; - -Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) { - ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities); - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) { - GnssCallback::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssLocationCb(const GnssLocationAidl& location) { - GnssCallback::gnssLocationCbImpl<GnssLocationAidl>(location); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) { - // In AIDL v1, if no listener is registered, do not report nmea to the framework. - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() <= 1) { - if (!isNmeaRegistered) { - return Status::ok(); - } - } - JNIEnv* env = getJniEnv(); - /* - * The Java code will call back to read these values. - * We do this to avoid creating unnecessary String objects. - */ - GnssCallback::sNmeaString = nmea.c_str(); - GnssCallback::sNmeaStringLength = nmea.size(); - - env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssAcquireWakelockCb() { - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssReleaseWakelockCb() { - release_wake_lock(WAKE_LOCK_NAME); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) { - ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str()); - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); - jstring jstringName = env->NewStringUTF(info.name.c_str()); - env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); - if (jstringName) { - env->DeleteLocalRef(jstringName); - } - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssRequestTimeCb() { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - -Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss, - const bool isUserEmergency) { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), - boolToJbool(isUserEmergency)); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); -} - /* * GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication * interface. @@ -756,35 +196,6 @@ Status GnssPowerIndicationCallback::gnssPowerStatsCb(const GnssPowerStats& data) } /* - * GnssPsdsCallback class implements the callback methods for the IGnssPsds - * interface. - */ -struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback { - Status downloadRequestCb(PsdsType psdsType) override { - ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType)); - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Status::ok(); - } -}; - -/** - * GnssXtraCallback class implements the callback methods for the IGnssXtra - * interface. - */ -class GnssXtraCallback : public IGnssXtraCallback { - Return<void> downloadRequestCb() override; -}; - -Return<void> GnssXtraCallback::downloadRequestCb() { - JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - return Void(); -} - -/* * GnssNiCallback implements callback methods required by the IGnssNi interface. */ struct GnssNiCallback : public IGnssNiCallback { @@ -822,41 +233,7 @@ Return<void> GnssNiCallback::niNotifyCb( /* Initializes the GNSS service handle. */ static void android_location_gnss_hal_GnssNative_set_gps_service_handle() { - gnssHalAidl = waitForVintfService<IGnssAidl>(); - if (gnssHalAidl != nullptr) { - ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion()); - if (gnssHalAidl->getInterfaceVersion() >= 2) { - return; - } - } - - ALOGD("Trying IGnss_V2_1::getService()"); - gnssHal_V2_1 = IGnss_V2_1::getService(); - if (gnssHal_V2_1 != nullptr) { - gnssHal = gnssHal_V2_1; - gnssHal_V2_0 = gnssHal_V2_1; - gnssHal_V1_1 = gnssHal_V2_1; - gnssHal = gnssHal_V2_1; - return; - } - - ALOGD("gnssHal 2.1 was null, trying 2.0"); - gnssHal_V2_0 = IGnss_V2_0::getService(); - if (gnssHal_V2_0 != nullptr) { - gnssHal = gnssHal_V2_0; - gnssHal_V1_1 = gnssHal_V2_0; - return; - } - - ALOGD("gnssHal 2.0 was null, trying 1.1"); - gnssHal_V1_1 = IGnss_V1_1::getService(); - if (gnssHal_V1_1 != nullptr) { - gnssHal = gnssHal_V1_1; - return; - } - - ALOGD("gnssHal 1.1 was null, trying 1.0"); - gnssHal = IGnss_V1_0::getService(); + gnssHal = std::make_unique<gnss::GnssHal>(); } /* One time initialization at system boot */ @@ -865,21 +242,10 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc android_location_gnss_hal_GnssNative_set_gps_service_handle(); // Cache methodIDs and class IDs. - method_reportLocation = env->GetMethodID(clazz, "reportLocation", - "(ZLandroid/location/Location;)V"); - method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); - method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V"); - method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); - method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V"); - method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V"); - method_setGnssHardwareModelName = env->GetMethodID(clazz, "setGnssHardwareModelName", - "(Ljava/lang/String;)V"); - method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V"); + method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;II)V"); - method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V"); - method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V"); - method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V"); + method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification", "(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V"); method_reportGnssPowerStats = @@ -894,17 +260,19 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass); method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V"); + gnss::AGnss_class_init_once(env, clazz); + gnss::AGnssRil_class_init_once(env, clazz); + gnss::Gnss_class_init_once(env, clazz); gnss::GnssAntennaInfo_class_init_once(env, clazz); gnss::GnssBatching_class_init_once(env, clazz); gnss::GnssConfiguration_class_init_once(env); gnss::GnssGeofence_class_init_once(env, clazz); gnss::GnssMeasurement_class_init_once(env, clazz); gnss::GnssNavigationMessage_class_init_once(env, clazz); + gnss::GnssPsds_class_init_once(env, clazz); gnss::GnssVisibilityControl_class_init_once(env, clazz); gnss::MeasurementCorrections_class_init_once(env, clazz); gnss::MeasurementCorrectionsCallback_class_init_once(env, clazz); - gnss::AGnss_class_init_once(env, clazz); - gnss::AGnssRil_class_init_once(env, clazz); gnss::Utils_class_init_once(env); } @@ -923,313 +291,26 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject android_location_gnss_hal_GnssNative_set_gps_service_handle(); } - if (gnssHal == nullptr && gnssHalAidl == nullptr) { + if (gnssHal == nullptr || !gnssHal->isSupported()) { ALOGE("Unable to get GPS service\n"); return; } - // TODO: linkToDeath for AIDL HAL - - if (gnssHal != nullptr) { - gnssHalDeathRecipient = new GnssDeathRecipient(); - hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0); - if (!linked.isOk()) { - ALOGE("Transaction error in linking to GnssHAL death: %s", - linked.description().c_str()); - } else if (!linked) { - ALOGW("Unable to link to GnssHal death notifications"); - } else { - ALOGD("Link to death notification successful"); - } - } - - if (gnssHalAidl != nullptr) { - sp<IGnssPsdsAidl> gnssPsdsAidl; - auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl); - if (status.isOk()) { - gnssPsdsAidlIface = gnssPsdsAidl; - } else { - ALOGD("Unable to get a handle to PSDS AIDL interface."); - } - } else if (gnssHal != nullptr) { - auto gnssXtra = gnssHal->getExtensionXtra(); - if (!gnssXtra.isOk()) { - ALOGD("Unable to get a handle to Xtra"); - } else { - gnssXtraIface = gnssXtra; - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<IAGnssRilAidl> agnssRilAidl; - auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl); - if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) { - agnssRilIface = std::make_unique<gnss::AGnssRil>(agnssRilAidl); - } - } else if (gnssHal_V2_0 != nullptr) { - auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0(); - if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) { - agnssRilIface = std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0); - } - } else if (gnssHal != nullptr) { - auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil(); - if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) { - agnssRilIface = std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<IAGnssAidl> agnssAidl; - auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl); - if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) { - agnssIface = std::make_unique<gnss::AGnss>(agnssAidl); - } - } else if (gnssHal_V2_0 != nullptr) { - auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0(); - if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) { - agnssIface = std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0); - } - } else if (gnssHal != nullptr) { - auto agnss_V1_0 = gnssHal->getExtensionAGnss(); - if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) { - agnssIface = std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage; - auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage); - if (checkAidlStatus(status, - "Unable to get a handle to GnssNavigationMessage AIDL interface.")) { - gnssNavigationMessageIface = - std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage); - } - } else if (gnssHal != nullptr) { - auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage(); - if (checkHidlReturn(gnssNavigationMessage, - "Unable to get a handle to GnssNavigationMessage interface.")) { - gnssNavigationMessageIface = - std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage); - } - } - - // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means, - // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement - // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement - // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement - // 1.0@IGnss is paired with 1.0@IGnssMeasurement - gnssMeasurementIface = nullptr; - if (gnssHalAidl != nullptr) { - sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement; - auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement); - if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) { - gnssMeasurementIface = - std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement); - } - } - if (gnssHal_V2_1 != nullptr && gnssMeasurementIface == nullptr) { - auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1(); - if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) { - gnssMeasurementIface = - std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement); - } - } - if (gnssHal_V2_0 != nullptr && gnssMeasurementIface == nullptr) { - auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); - if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) { - gnssMeasurementIface = - std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement); - } - } - if (gnssHal_V1_1 != nullptr && gnssMeasurementIface == nullptr) { - auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); - if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) { - gnssMeasurementIface = - std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement); - } - } - if (gnssHal != nullptr && gnssMeasurementIface == nullptr) { - auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement(); - if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) { - gnssMeasurementIface = - std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl; - auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl); - if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) { - gnssAntennaInfoIface = std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl); - } - } else if (gnssHal_V2_1 != nullptr) { - auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo(); - if (checkHidlReturn(gnssAntennaInfo_V2_1, - "Unable to get a handle to GnssAntennaInfo_V2_1")) { - gnssAntennaInfoIface = - std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface> - gnssMeasurementCorrectionsAidl; - auto status = - gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl); - if (checkAidlStatus(status, - "Unable to get a handle to GnssVisibilityControl AIDL interface.")) { - gnssMeasurementCorrectionsIface = - std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>( - gnssMeasurementCorrectionsAidl); - } - } - if (gnssHal_V2_1 != nullptr && gnssMeasurementCorrectionsIface == nullptr) { - auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1(); - if (checkHidlReturn(gnssCorrections, - "Unable to get a handle to GnssMeasurementCorrections HIDL " - "interface")) { - gnssMeasurementCorrectionsIface = - std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections); - } - } - if (gnssHal_V2_0 != nullptr && gnssMeasurementCorrectionsIface == nullptr) { - auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections(); - if (checkHidlReturn(gnssCorrections, - "Unable to get a handle to GnssMeasurementCorrections HIDL " - "interface")) { - gnssMeasurementCorrectionsIface = - std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections); - } - } - - // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means, - // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug - // 1.0@IGnss is paired with 1.0@IGnssDebug - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<IGnssDebugAidl> gnssDebugAidl; - auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl); - if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) { - gnssDebugIface = std::make_unique<gnss::GnssDebug>(gnssDebugAidl); - } - } - if (gnssHal_V2_0 != nullptr && gnssDebugIface == nullptr) { - auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0(); - if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) { - gnssDebugIface = std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0); - } - } - if (gnssHal != nullptr && gnssDebugIface == nullptr) { - auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug(); - if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) { - gnssDebugIface = std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0); - } - } - - if (gnssHal != nullptr) { - auto gnssNi = gnssHal->getExtensionGnssNi(); - if (!gnssNi.isOk()) { - ALOGD("Unable to get a handle to GnssNi"); - } else { - gnssNiIface = gnssNi; - } - } - - if (gnssHalAidl != nullptr) { - sp<IGnssConfigurationAidl> gnssConfigurationAidl; - auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl); - if (checkAidlStatus(status, - "Unable to get a handle to GnssConfiguration AIDL interface.")) { - gnssConfigurationIface = - std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl); - } - } else if (gnssHal_V2_1 != nullptr) { - auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1(); - if (checkHidlReturn(gnssConfiguration, - "Unable to get a handle to GnssConfiguration_V2_1")) { - gnssConfigurationIface = - std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration); - } - } else if (gnssHal_V2_0 != nullptr) { - auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0(); - if (checkHidlReturn(gnssConfiguration, - "Unable to get a handle to GnssConfiguration_V2_0")) { - gnssConfigurationIface = - std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration); - } - } else if (gnssHal_V1_1 != nullptr) { - auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1(); - if (checkHidlReturn(gnssConfiguration, - "Unable to get a handle to GnssConfiguration_V1_1")) { - gnssConfigurationIface = - std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration); - } - } else if (gnssHal != nullptr) { - auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration(); - if (checkHidlReturn(gnssConfiguration, - "Unable to get a handle to GnssConfiguration_V1_0")) { - gnssConfigurationIface = - std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<hardware::gnss::IGnssGeofence> gnssGeofence; - auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence); - if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) { - gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence); - } - } else if (gnssHal != nullptr) { - auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing(); - if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) { - gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl; - auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl); - if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) { - gnssBatchingIface = std::make_unique<gnss::GnssBatching>(gnssBatchingAidl); - } - } else if (gnssHal_V2_0 != nullptr) { - auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0(); - if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) { - gnssBatchingIface = std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0); - } - } - if (gnssHal != nullptr && gnssBatchingIface == nullptr) { - auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching(); - if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) { - gnssBatchingIface = std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0); - } - } - - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl> - gnssVisibilityControlAidl; - auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl); - if (checkAidlStatus(status, - "Unable to get a handle to GnssVisibilityControl AIDL interface.")) { - gnssVisibilityControlIface = - std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl); - } - } else if (gnssHal_V2_0 != nullptr) { - auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl(); - if (checkHidlReturn(gnssVisibilityControlHidl, - "Unable to get a handle to GnssVisibilityControl HIDL interface")) { - gnssVisibilityControlIface = - std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl); - } - } - - if (gnssHalAidl != nullptr) { - sp<IGnssPowerIndication> gnssPowerIndication; - auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication); - if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication interface.")) { - gnssPowerIndicationIface = gnssPowerIndication; - } - } + gnssHal->linkToDeath(); + gnssPsdsIface = gnssHal->getGnssPsdsInterface(); + agnssRilIface = gnssHal->getAGnssRilInterface(); + agnssIface = gnssHal->getAGnssInterface(); + gnssNavigationMessageIface = gnssHal->getGnssNavigationMessageInterface(); + gnssMeasurementIface = gnssHal->getGnssMeasurementInterface(); + gnssAntennaInfoIface = gnssHal->getGnssAntennaInfoInterface(); + gnssMeasurementCorrectionsIface = gnssHal->getMeasurementCorrectionsInterface(); + gnssDebugIface = gnssHal->getGnssDebugInterface(); + gnssNiIface = gnssHal->getGnssNiInterface(); + gnssConfigurationIface = gnssHal->getGnssConfigurationInterface(); + gnssGeofencingIface = gnssHal->getGnssGeofenceInterface(); + gnssBatchingIface = gnssHal->getGnssBatchingInterface(); + gnssVisibilityControlIface = gnssHal->getGnssVisibilityControlInterface(); + gnssPowerIndicationIface = gnssHal->getGnssPowerIndicationInterface(); if (mCallbacksObj) { ALOGE("Callbacks already initialized"); @@ -1239,7 +320,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject } static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) { - return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE; + return (gnssHal != nullptr && gnssHal->isSupported()) ? JNI_TRUE : JNI_FALSE; } static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported( @@ -1268,52 +349,18 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl /* * Fail if the main interface fails to initialize */ - if (gnssHal == nullptr && gnssHalAidl == nullptr) { + if (!gnssHal->isSupported()) { ALOGE("Unable to initialize GNSS HAL."); return JNI_FALSE; } - // Set top level IGnss.hal callback. - if (gnssHal != nullptr) { - Return<bool> result = false; - sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallback(); - if (gnssHal_V2_1 != nullptr) { - result = gnssHal_V2_1->setCallback_2_1(gnssCbIface); - } else if (gnssHal_V2_0 != nullptr) { - result = gnssHal_V2_0->setCallback_2_0(gnssCbIface); - } else if (gnssHal_V1_1 != nullptr) { - result = gnssHal_V1_1->setCallback_1_1(gnssCbIface); - } else { - result = gnssHal->setCallback(gnssCbIface); - } - if (!checkHidlReturn(result, "IGnss setCallback() failed.")) { - return JNI_FALSE; - } - } - - if (gnssHalAidl != nullptr) { - sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl(); - auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl); - if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) { - return JNI_FALSE; - } - } + // Set top level IGnss HAL callback. + gnssHal->setCallback(); - // Set IGnssPsds or IGnssXtra callback. - if (gnssPsdsAidlIface != nullptr) { - sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl(); - auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl); - if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) { - gnssPsdsAidlIface = nullptr; - } - } else if (gnssXtraIface != nullptr) { - sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback(); - auto result = gnssXtraIface->setCallback(gnssXtraCbIface); - if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) { - gnssXtraIface = nullptr; - } else { - ALOGI("Unable to initialize IGnssXtra interface."); - } + // Set IGnssPsds callback. + if (gnssPsdsIface == nullptr || + !gnssPsdsIface->setCallback(std::make_unique<gnss::GnssPsdsCallback>())) { + ALOGI("Unable to initialize IGnssPsds interface."); } // Set IAGnss callback. @@ -1373,145 +420,47 @@ static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jcl } static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) { - if (gnssHalAidl != nullptr) { - auto status = gnssHalAidl->close(); - checkAidlStatus(status, "IGnssAidl close() failed."); - } - - if (gnssHal != nullptr) { - auto result = gnssHal->cleanup(); - checkHidlReturn(result, "IGnss cleanup() failed."); - } + gnssHal->close(); } static jboolean android_location_gnss_hal_GnssNative_set_position_mode( JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - IGnssAidl::PositionModeOptions options; - options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode); - options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence); - options.minIntervalMs = min_interval; - options.preferredAccuracyMeters = preferred_accuracy; - options.preferredTimeMs = preferred_time; - options.lowPowerMode = low_power_mode; - auto status = gnssHalAidl->setPositionMode(options); - return checkAidlStatus(status, "IGnssAidl setPositionMode() failed."); - } - - Return<bool> result = false; - if (gnssHal_V1_1 != nullptr) { - result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode), - static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence), - min_interval, - preferred_accuracy, - preferred_time, - low_power_mode); - } else if (gnssHal != nullptr) { - result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode), - static_cast<IGnss_V1_0::GnssPositionRecurrence>(recurrence), - min_interval, - preferred_accuracy, - preferred_time); - } - - return checkHidlReturn(result, "IGnss setPositionMode() failed."); + return gnssHal->setPositionMode(mode, recurrence, min_interval, preferred_accuracy, + preferred_time, low_power_mode); } static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->start(); - return checkAidlStatus(status, "IGnssAidl start() failed."); - } - - if (gnssHal == nullptr) { - return JNI_FALSE; - } - - auto result = gnssHal->start(); - return checkHidlReturn(result, "IGnss start() failed."); + return gnssHal->start(); } static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->stop(); - return checkAidlStatus(status, "IGnssAidl stop() failed."); - } - - if (gnssHal == nullptr) { - return JNI_FALSE; - } - - auto result = gnssHal->stop(); - return checkHidlReturn(result, "IGnss stop() failed."); + return gnssHal->stop(); } static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */, jclass) { - isSvStatusRegistered = true; - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->startSvStatus(); - return checkAidlStatus(status, "IGnssAidl startSvStatus() failed."); - } - if (gnssHal == nullptr) { - return JNI_FALSE; - } - return JNI_TRUE; + return gnssHal->startSvStatus(); } static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */, jclass) { - isSvStatusRegistered = false; - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->stopSvStatus(); - return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed."); - } - if (gnssHal == nullptr) { - return JNI_FALSE; - } - return JNI_TRUE; + return gnssHal->stopSvStatus(); } static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection( JNIEnv* /* env */, jclass) { - isNmeaRegistered = true; - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->startNmea(); - return checkAidlStatus(status, "IGnssAidl startNmea() failed."); - } - if (gnssHal == nullptr) { - return JNI_FALSE; - } - return JNI_TRUE; + return gnssHal->startNmea(); } static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */, jclass) { - isNmeaRegistered = false; - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->stopNmea(); - return checkAidlStatus(status, "IGnssAidl stopNmea() failed."); - } - if (gnssHal == nullptr) { - return JNI_FALSE; - } - return JNI_TRUE; + return gnssHal->stopNmea(); } static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass, jint flags) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags)); - checkAidlStatus(status, "IGnssAidl deleteAidingData() failed."); - return; - } - - if (gnssHal == nullptr) { - return; - } - - auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags)); - checkHidlReturn(result, "IGnss deleteAidingData() failed."); + gnssHal->deleteAidingData(flags); } static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid( @@ -1535,30 +484,13 @@ static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass, jbyteArray nmeaArray, jint buffer_size) { - // this should only be called from within a call to reportNmea - jbyte* nmea = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(nmeaArray, 0)); - int length = GnssCallback::sNmeaStringLength; - if (length > buffer_size) - length = buffer_size; - memcpy(nmea, GnssCallback::sNmeaString, length); - env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); - return (jint) length; + return gnssHal->readNmea(nmeaArray, buffer_size); } static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time, jlong timeReference, jint uncertainty) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty); - checkAidlStatus(status, "IGnssAidl injectTime() failed."); - return; - } - - if (gnssHal == nullptr) { - return; - } - auto result = gnssHal->injectTime(time, timeReference, uncertainty); - checkHidlReturn(result, "IGnss injectTime() failed."); + gnssHal->injectTime(time, timeReference, uncertainty); } static void android_location_gnss_hal_GnssNative_inject_best_location( @@ -1568,58 +500,12 @@ static void android_location_gnss_hal_GnssNative_inject_best_location( jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, jdouble elapsedRealtimeUncertaintyNanos) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - GnssLocationAidl location = - createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, - altitudeMeters, speedMetersPerSec, bearingDegrees, - horizontalAccuracyMeters, verticalAccuracyMeters, - speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, - elapsedRealtimeFlags, elapsedRealtimeNanos, - elapsedRealtimeUncertaintyNanos); - auto status = gnssHalAidl->injectBestLocation(location); - checkAidlStatus(status, "IGnssAidl injectBestLocation() failed."); - return; - } - - if (gnssHal_V2_0 != nullptr) { - GnssLocation_V2_0 location = createGnssLocation_V2_0( - gnssLocationFlags, - latitudeDegrees, - longitudeDegrees, - altitudeMeters, - speedMetersPerSec, - bearingDegrees, - horizontalAccuracyMeters, - verticalAccuracyMeters, - speedAccuracyMetersPerSecond, - bearingAccuracyDegrees, - timestamp, - elapsedRealtimeFlags, - elapsedRealtimeNanos, - elapsedRealtimeUncertaintyNanos); - auto result = gnssHal_V2_0->injectBestLocation_2_0(location); - checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed."); - return; - } - - if (gnssHal_V1_1 != nullptr) { - GnssLocation_V1_0 location = createGnssLocation_V1_0( - gnssLocationFlags, - latitudeDegrees, - longitudeDegrees, - altitudeMeters, - speedMetersPerSec, - bearingDegrees, - horizontalAccuracyMeters, - verticalAccuracyMeters, - speedAccuracyMetersPerSecond, - bearingAccuracyDegrees, - timestamp); - auto result = gnssHal_V1_1->injectBestLocation(location); - checkHidlReturn(result, "IGnss injectBestLocation() failed."); - } - - ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available."); + gnssHal->injectBestLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, + elapsedRealtimeFlags, elapsedRealtimeNanos, + elapsedRealtimeUncertaintyNanos); } static void android_location_gnss_hal_GnssNative_inject_location( @@ -1629,51 +515,25 @@ static void android_location_gnss_hal_GnssNative_inject_location( jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, jdouble elapsedRealtimeUncertaintyNanos) { - if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - GnssLocationAidl location = - createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, - altitudeMeters, speedMetersPerSec, bearingDegrees, - horizontalAccuracyMeters, verticalAccuracyMeters, - speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, - elapsedRealtimeFlags, elapsedRealtimeNanos, - elapsedRealtimeUncertaintyNanos); - auto status = gnssHalAidl->injectLocation(location); - checkAidlStatus(status, "IGnssAidl injectLocation() failed."); - return; - } - - if (gnssHal == nullptr) { - return; - } - auto result = - gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters); - checkHidlReturn(result, "IGnss injectLocation() failed."); + gnssHal->injectLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters, + speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters, + verticalAccuracyMeters, speedAccuracyMetersPerSecond, + bearingAccuracyDegrees, timestamp, elapsedRealtimeFlags, + elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos); } static jboolean android_location_gnss_hal_GnssNative_supports_psds(JNIEnv* /* env */, jclass) { - return (gnssPsdsAidlIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE; + return (gnssPsdsIface != nullptr) ? JNI_TRUE : JNI_FALSE; } static void android_location_gnss_hal_GnssNative_inject_psds_data(JNIEnv* env, jclass, jbyteArray data, jint length, jint psdsType) { - if (gnssPsdsAidlIface == nullptr && gnssXtraIface == nullptr) { - ALOGE("%s: IGnssPsdsAidl or IGnssXtra interface not available.", __func__); + if (gnssPsdsIface == nullptr) { + ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__); return; } - - jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0)); - if (gnssPsdsAidlIface != nullptr) { - auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType), - std::vector<uint8_t>((const uint8_t*)bytes, - (const uint8_t*)bytes + - length)); - checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed."); - } else if (gnssXtraIface != nullptr) { - auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length)); - checkHidlReturn(result, "IGnssXtra injectXtraData() failed."); - } - env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); + gnssPsdsIface->injectPsdsData(data, length, psdsType); } static void android_location_GnssNetworkConnectivityHandler_agps_data_conn_open( diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp index e52df15b973a..0531ae2a8ccd 100644 --- a/services/core/jni/gnss/Android.bp +++ b/services/core/jni/gnss/Android.bp @@ -28,6 +28,8 @@ cc_library_shared { "AGnssRil.cpp", "AGnssRilCallback.cpp", "GnssAntennaInfo.cpp", + "Gnss.cpp", + "GnssCallback.cpp", "GnssAntennaInfoCallback.cpp", "GnssBatching.cpp", "GnssBatchingCallback.cpp", @@ -39,6 +41,8 @@ cc_library_shared { "GnssMeasurementCallback.cpp", "GnssNavigationMessage.cpp", "GnssNavigationMessageCallback.cpp", + "GnssPsds.cpp", + "GnssPsdsCallback.cpp", "GnssVisibilityControl.cpp", "GnssVisibilityControlCallback.cpp", "MeasurementCorrections.cpp", @@ -55,6 +59,7 @@ cc_defaults { "libhidlbase", "liblog", "libnativehelper", + "libhardware_legacy", "libutils", "android.hardware.gnss-V2-cpp", "android.hardware.gnss@1.0", diff --git a/services/core/jni/gnss/Gnss.cpp b/services/core/jni/gnss/Gnss.cpp new file mode 100644 index 000000000000..f6459eaa444e --- /dev/null +++ b/services/core/jni/gnss/Gnss.cpp @@ -0,0 +1,759 @@ +/* + * 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. + */ + +// Define LOG_TAG before <log/log.h> to overwrite the default value. +#define LOG_TAG "GnssJni" + +#include "Gnss.h" + +#include <binder/IServiceManager.h> + +#include "Utils.h" + +namespace android::gnss { + +using hardware::Return; + +using GnssLocationAidl = hardware::gnss::GnssLocation; +using GnssLocation_V1_0 = hardware::gnss::V1_0::GnssLocation; +using GnssLocation_V2_0 = hardware::gnss::V2_0::GnssLocation; +using IAGnssAidl = hardware::gnss::IAGnss; +using IAGnssRilAidl = hardware::gnss::IAGnssRil; +using IGnssAidl = hardware::gnss::IGnss; +using IGnss_V1_0 = hardware::gnss::V1_0::IGnss; +using IGnss_V1_1 = hardware::gnss::V1_1::IGnss; +using IGnss_V2_0 = hardware::gnss::V2_0::IGnss; +using IGnss_V2_1 = hardware::gnss::V2_1::IGnss; +using IGnssAntennaInfoAidl = hardware::gnss::IGnssAntennaInfo; +using IGnssCallbackAidl = hardware::gnss::IGnssCallback; +using IGnssCallback_V1_0 = hardware::gnss::V1_0::IGnssCallback; +using IGnssCallback_V2_0 = hardware::gnss::V2_0::IGnssCallback; +using IGnssCallback_V2_1 = hardware::gnss::V2_1::IGnssCallback; +using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration; +using IGnssDebugAidl = hardware::gnss::IGnssDebug; +using android::hardware::gnss::IGnssPsds; + +namespace { + +GnssLocationAidl createGnssLocation(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, + jfloat bearingAccuracyDegrees, jlong timestamp, + jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos) { + GnssLocationAidl location; + location.gnssLocationFlags = static_cast<int>(gnssLocationFlags); + location.latitudeDegrees = static_cast<double>(latitudeDegrees); + location.longitudeDegrees = static_cast<double>(longitudeDegrees); + location.altitudeMeters = static_cast<double>(altitudeMeters); + location.speedMetersPerSec = static_cast<double>(speedMetersPerSec); + location.bearingDegrees = static_cast<double>(bearingDegrees); + location.horizontalAccuracyMeters = static_cast<double>(horizontalAccuracyMeters); + location.verticalAccuracyMeters = static_cast<double>(verticalAccuracyMeters); + location.speedAccuracyMetersPerSecond = static_cast<double>(speedAccuracyMetersPerSecond); + location.bearingAccuracyDegrees = static_cast<double>(bearingAccuracyDegrees); + location.timestampMillis = static_cast<uint64_t>(timestamp); + + location.elapsedRealtime.flags = static_cast<int>(elapsedRealtimeFlags); + location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos); + location.elapsedRealtime.timeUncertaintyNs = + static_cast<double>(elapsedRealtimeUncertaintyNanos); + + return location; +} + +GnssLocation_V1_0 createGnssLocation_V1_0(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, + jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, + jfloat bearingAccuracyDegrees, jlong timestamp) { + GnssLocation_V1_0 location; + location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags); + location.latitudeDegrees = static_cast<double>(latitudeDegrees); + location.longitudeDegrees = static_cast<double>(longitudeDegrees); + location.altitudeMeters = static_cast<double>(altitudeMeters); + location.speedMetersPerSec = static_cast<float>(speedMetersPerSec); + location.bearingDegrees = static_cast<float>(bearingDegrees); + location.horizontalAccuracyMeters = static_cast<float>(horizontalAccuracyMeters); + location.verticalAccuracyMeters = static_cast<float>(verticalAccuracyMeters); + location.speedAccuracyMetersPerSecond = static_cast<float>(speedAccuracyMetersPerSecond); + location.bearingAccuracyDegrees = static_cast<float>(bearingAccuracyDegrees); + location.timestamp = static_cast<uint64_t>(timestamp); + + return location; +} + +GnssLocation_V2_0 createGnssLocation_V2_0(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, + jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, + jfloat bearingAccuracyDegrees, jlong timestamp, + jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos) { + GnssLocation_V2_0 location; + location.v1_0 = createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, + timestamp); + + location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags); + location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos); + location.elapsedRealtime.timeUncertaintyNs = + static_cast<uint64_t>(elapsedRealtimeUncertaintyNanos); + + return location; +} + +} // anonymous namespace + +// Implementation of GnssHal, which unifies all versions of GNSS HALs + +GnssHal::GnssHal() { + gnssHalAidl = waitForVintfService<IGnssAidl>(); + if (gnssHalAidl != nullptr) { + ALOGD("Successfully got GNSS AIDL handle. Version=%d.", gnssHalAidl->getInterfaceVersion()); + if (gnssHalAidl->getInterfaceVersion() >= 2) { + return; + } + } + + ALOGD("Trying IGnss_V2_1::getService()"); + gnssHal_V2_1 = IGnss_V2_1::getService(); + if (gnssHal_V2_1 != nullptr) { + gnssHal_V2_0 = gnssHal_V2_1; + gnssHal_V1_1 = gnssHal_V2_1; + gnssHal = gnssHal_V2_1; + return; + } + + ALOGD("gnssHal 2.1 was null, trying 2.0"); + gnssHal_V2_0 = IGnss_V2_0::getService(); + if (gnssHal_V2_0 != nullptr) { + gnssHal_V1_1 = gnssHal_V2_0; + gnssHal = gnssHal_V2_0; + return; + } + + ALOGD("gnssHal 2.0 was null, trying 1.1"); + gnssHal_V1_1 = IGnss_V1_1::getService(); + if (gnssHal_V1_1 != nullptr) { + gnssHal = gnssHal_V1_1; + return; + } + + ALOGD("gnssHal 1.1 was null, trying 1.0"); + gnssHal = IGnss_V1_0::getService(); +} + +jboolean GnssHal::isSupported() { + return (gnssHalAidl != nullptr || gnssHal != nullptr) ? JNI_TRUE : JNI_FALSE; +} + +void GnssHal::linkToDeath() { + // TODO: linkToDeath for AIDL HAL + + if (gnssHal != nullptr) { + gnssHalDeathRecipient = new GnssDeathRecipient(); + hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to GnssHAL death: %s", + linked.description().c_str()); + } else if (!linked) { + ALOGW("Unable to link to GnssHal death notifications"); + } else { + ALOGD("Link to death notification successful"); + } + } +} + +jboolean GnssHal::setCallback() { + if (gnssHalAidl != nullptr) { + sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl(); + auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl); + if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) { + return JNI_FALSE; + } + } + if (gnssHal != nullptr) { + Return<bool> result = false; + sp<IGnssCallback_V2_1> gnssCbIface = new GnssCallbackHidl(); + if (gnssHal_V2_1 != nullptr) { + result = gnssHal_V2_1->setCallback_2_1(gnssCbIface); + } else if (gnssHal_V2_0 != nullptr) { + result = gnssHal_V2_0->setCallback_2_0(gnssCbIface); + } else if (gnssHal_V1_1 != nullptr) { + result = gnssHal_V1_1->setCallback_1_1(gnssCbIface); + } else { + result = gnssHal->setCallback(gnssCbIface); + } + if (!checkHidlReturn(result, "IGnss setCallback() failed.")) { + return JNI_FALSE; + } + } + return JNI_TRUE; +} + +void GnssHal::close() { + if (gnssHalAidl != nullptr) { + auto status = gnssHalAidl->close(); + checkAidlStatus(status, "IGnssAidl close() failed."); + } + + if (gnssHal != nullptr) { + auto result = gnssHal->cleanup(); + checkHidlReturn(result, "IGnss cleanup() failed."); + } +} + +jboolean GnssHal::start() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->start(); + return checkAidlStatus(status, "IGnssAidl start() failed."); + } + + if (gnssHal == nullptr) { + return JNI_FALSE; + } + + auto result = gnssHal->start(); + return checkHidlReturn(result, "IGnss start() failed."); +} + +jboolean GnssHal::stop() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->stop(); + return checkAidlStatus(status, "IGnssAidl stop() failed."); + } + + if (gnssHal == nullptr) { + return JNI_FALSE; + } + + auto result = gnssHal->stop(); + return checkHidlReturn(result, "IGnss stop() failed."); +} + +jboolean GnssHal::startSvStatus() { + isSvStatusRegistered = true; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->startSvStatus(); + return checkAidlStatus(status, "IGnssAidl startSvStatus() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +jboolean GnssHal::stopSvStatus() { + isSvStatusRegistered = false; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->stopSvStatus(); + return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +jboolean GnssHal::startNmea() { + isNmeaRegistered = true; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->startNmea(); + return checkAidlStatus(status, "IGnssAidl startNmea() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +jboolean GnssHal::stopNmea() { + isNmeaRegistered = false; + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->stopNmea(); + return checkAidlStatus(status, "IGnssAidl stopNmea() failed."); + } + if (gnssHal == nullptr) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +jint GnssHal::readNmea(jbyteArray& nmeaArray, jint& buffer_size) { + // this should only be called from within a call to reportNmea + JNIEnv* env = getJniEnv(); + jbyte* nmea = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(nmeaArray, 0)); + int length = GnssCallbackHidl::sNmeaStringLength; + if (length > buffer_size) { + length = buffer_size; + } + memcpy(nmea, GnssCallbackHidl::sNmeaString, length); + env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); + return (jint)length; +} + +jboolean GnssHal::setPositionMode(jint mode, jint recurrence, jint min_interval, + jint preferred_accuracy, jint preferred_time, + jboolean low_power_mode) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + IGnssAidl::PositionModeOptions options; + options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode); + options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence); + options.minIntervalMs = min_interval; + options.preferredAccuracyMeters = preferred_accuracy; + options.preferredTimeMs = preferred_time; + options.lowPowerMode = low_power_mode; + auto status = gnssHalAidl->setPositionMode(options); + return checkAidlStatus(status, "IGnssAidl setPositionMode() failed."); + } + + Return<bool> result = false; + if (gnssHal_V1_1 != nullptr) { + result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode), + static_cast<IGnss_V1_0::GnssPositionRecurrence>( + recurrence), + min_interval, preferred_accuracy, preferred_time, + low_power_mode); + } else if (gnssHal != nullptr) { + result = gnssHal->setPositionMode(static_cast<IGnss_V1_0::GnssPositionMode>(mode), + static_cast<IGnss_V1_0::GnssPositionRecurrence>( + recurrence), + min_interval, preferred_accuracy, preferred_time); + } + return checkHidlReturn(result, "IGnss setPositionMode() failed."); +} + +void GnssHal::deleteAidingData(jint flags) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->deleteAidingData(static_cast<IGnssAidl::GnssAidingData>(flags)); + checkAidlStatus(status, "IGnssAidl deleteAidingData() failed."); + return; + } + + if (gnssHal == nullptr) { + return; + } + + auto result = gnssHal->deleteAidingData(static_cast<IGnss_V1_0::GnssAidingData>(flags)); + checkHidlReturn(result, "IGnss deleteAidingData() failed."); +} + +void GnssHal::injectTime(jlong time, jlong timeReference, jint uncertainty) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + auto status = gnssHalAidl->injectTime(time, timeReference, uncertainty); + checkAidlStatus(status, "IGnssAidl injectTime() failed."); + return; + } + + if (gnssHal == nullptr) { + return; + } + auto result = gnssHal->injectTime(time, timeReference, uncertainty); + checkHidlReturn(result, "IGnss injectTime() failed."); +} + +void GnssHal::injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, + jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + GnssLocationAidl location = + createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, + elapsedRealtimeFlags, elapsedRealtimeNanos, + elapsedRealtimeUncertaintyNanos); + auto status = gnssHalAidl->injectLocation(location); + checkAidlStatus(status, "IGnssAidl injectLocation() failed."); + return; + } + + if (gnssHal == nullptr) { + return; + } + auto result = + gnssHal->injectLocation(latitudeDegrees, longitudeDegrees, horizontalAccuracyMeters); + checkHidlReturn(result, "IGnss injectLocation() failed."); +} + +void GnssHal::injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, + jlong timestamp, jint elapsedRealtimeFlags, + jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos) { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + GnssLocationAidl location = + createGnssLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, + elapsedRealtimeFlags, elapsedRealtimeNanos, + elapsedRealtimeUncertaintyNanos); + auto status = gnssHalAidl->injectBestLocation(location); + checkAidlStatus(status, "IGnssAidl injectBestLocation() failed."); + return; + } + + if (gnssHal_V2_0 != nullptr) { + GnssLocation_V2_0 location = + createGnssLocation_V2_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, + timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos, + elapsedRealtimeUncertaintyNanos); + auto result = gnssHal_V2_0->injectBestLocation_2_0(location); + checkHidlReturn(result, "IGnss injectBestLocation_2_0() failed."); + return; + } + + if (gnssHal_V1_1 != nullptr) { + GnssLocation_V1_0 location = + createGnssLocation_V1_0(gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, + timestamp); + auto result = gnssHal_V1_1->injectBestLocation(location); + checkHidlReturn(result, "IGnss injectBestLocation() failed."); + return; + } + + ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available."); +} + +std::unique_ptr<AGnssInterface> GnssHal::getAGnssInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<IAGnssAidl> agnssAidl; + auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl); + if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) { + return std::make_unique<gnss::AGnss>(agnssAidl); + } + } else if (gnssHal_V2_0 != nullptr) { + auto agnss_V2_0 = gnssHal_V2_0->getExtensionAGnss_2_0(); + if (checkHidlReturn(agnss_V2_0, "Unable to get a handle to AGnss_V2_0")) { + return std::make_unique<gnss::AGnss_V2_0>(agnss_V2_0); + } + } else if (gnssHal != nullptr) { + auto agnss_V1_0 = gnssHal->getExtensionAGnss(); + if (checkHidlReturn(agnss_V1_0, "Unable to get a handle to AGnss_V1_0")) { + return std::make_unique<gnss::AGnss_V1_0>(agnss_V1_0); + } + } + return nullptr; +} + +std::unique_ptr<AGnssRilInterface> GnssHal::getAGnssRilInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<IAGnssRilAidl> agnssRilAidl; + auto status = gnssHalAidl->getExtensionAGnssRil(&agnssRilAidl); + if (checkAidlStatus(status, "Unable to get a handle to AGnssRil interface.")) { + return std::make_unique<gnss::AGnssRil>(agnssRilAidl); + } + } else if (gnssHal_V2_0 != nullptr) { + auto agnssRil_V2_0 = gnssHal_V2_0->getExtensionAGnssRil_2_0(); + if (checkHidlReturn(agnssRil_V2_0, "Unable to get a handle to AGnssRil_V2_0")) { + return std::make_unique<gnss::AGnssRil_V2_0>(agnssRil_V2_0); + } + } else if (gnssHal != nullptr) { + auto agnssRil_V1_0 = gnssHal->getExtensionAGnssRil(); + if (checkHidlReturn(agnssRil_V1_0, "Unable to get a handle to AGnssRil_V1_0")) { + return std::make_unique<gnss::AGnssRil_V1_0>(agnssRil_V1_0); + } + } + return nullptr; +} + +std::unique_ptr<GnssNavigationMessageInterface> GnssHal::getGnssNavigationMessageInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<hardware::gnss::IGnssNavigationMessageInterface> gnssNavigationMessage; + auto status = gnssHalAidl->getExtensionGnssNavigationMessage(&gnssNavigationMessage); + if (checkAidlStatus(status, + "Unable to get a handle to GnssNavigationMessage AIDL interface.")) { + return std::make_unique<gnss::GnssNavigationMessageAidl>(gnssNavigationMessage); + } + } else if (gnssHal != nullptr) { + auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage(); + if (checkHidlReturn(gnssNavigationMessage, + "Unable to get a handle to GnssNavigationMessage interface.")) { + return std::make_unique<gnss::GnssNavigationMessageHidl>(gnssNavigationMessage); + } + } + return nullptr; +} + +std::unique_ptr<GnssMeasurementInterface> GnssHal::getGnssMeasurementInterface() { + // Allow all causal combinations between IGnss.hal and IGnssMeasurement.hal. That means, + // 2.1@IGnss can be paired with {1.0, 1,1, 2.0, 2.1}@IGnssMeasurement + // 2.0@IGnss can be paired with {1.0, 1,1, 2.0}@IGnssMeasurement + // 1.1@IGnss can be paired {1.0, 1.1}@IGnssMeasurement + // 1.0@IGnss is paired with 1.0@IGnssMeasurement + if (gnssHalAidl != nullptr) { + sp<hardware::gnss::IGnssMeasurementInterface> gnssMeasurement; + auto status = gnssHalAidl->getExtensionGnssMeasurement(&gnssMeasurement); + if (checkAidlStatus(status, "Unable to get a handle to GnssMeasurement AIDL interface.")) { + return std::make_unique<android::gnss::GnssMeasurement>(gnssMeasurement); + } + } + if (gnssHal_V2_1 != nullptr) { + auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1(); + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) { + return std::make_unique<android::gnss::GnssMeasurement_V2_1>(gnssMeasurement); + } + } + if (gnssHal_V2_0 != nullptr) { + auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) { + return std::make_unique<android::gnss::GnssMeasurement_V2_0>(gnssMeasurement); + } + } + if (gnssHal_V1_1 != nullptr) { + auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) { + return std::make_unique<android::gnss::GnssMeasurement_V1_1>(gnssMeasurement); + } + } + if (gnssHal != nullptr) { + auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement(); + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) { + return std::make_unique<android::gnss::GnssMeasurement_V1_0>(gnssMeasurement); + } + } + return nullptr; +} + +std::unique_ptr<GnssDebugInterface> GnssHal::getGnssDebugInterface() { + // Allow all causal combinations between IGnss.hal and IGnssDebug.hal. That means, + // 2.0@IGnss can be paired with {1.0, 2.0}@IGnssDebug + // 1.0@IGnss is paired with 1.0@IGnssDebug + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<IGnssDebugAidl> gnssDebugAidl; + auto status = gnssHalAidl->getExtensionGnssDebug(&gnssDebugAidl); + if (checkAidlStatus(status, "Unable to get a handle to GnssDebug interface.")) { + return std::make_unique<gnss::GnssDebug>(gnssDebugAidl); + } + } + if (gnssHal_V2_0 != nullptr) { + auto gnssDebug_V2_0 = gnssHal_V2_0->getExtensionGnssDebug_2_0(); + if (checkHidlReturn(gnssDebug_V2_0, "Unable to get a handle to GnssDebug_V2_0.")) { + return std::make_unique<gnss::GnssDebug_V2_0>(gnssDebug_V2_0); + } + } + if (gnssHal != nullptr) { + auto gnssDebug_V1_0 = gnssHal->getExtensionGnssDebug(); + if (checkHidlReturn(gnssDebug_V1_0, "Unable to get a handle to GnssDebug_V1_0.")) { + return std::make_unique<gnss::GnssDebug_V1_0>(gnssDebug_V1_0); + } + } + return nullptr; +} + +std::unique_ptr<GnssConfigurationInterface> GnssHal::getGnssConfigurationInterface() { + if (gnssHalAidl != nullptr) { + sp<IGnssConfigurationAidl> gnssConfigurationAidl; + auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl); + if (checkAidlStatus(status, + "Unable to get a handle to GnssConfiguration AIDL interface.")) { + return std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl); + } + } else if (gnssHal_V2_1 != nullptr) { + auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1(); + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V2_1")) { + return std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration); + } + } else if (gnssHal_V2_0 != nullptr) { + auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0(); + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V2_0")) { + return std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration); + } + } else if (gnssHal_V1_1 != nullptr) { + auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1(); + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V1_1")) { + return std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration); + } + } else if (gnssHal != nullptr) { + auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration(); + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V1_0")) { + return std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration); + } + } + return nullptr; +} + +std::unique_ptr<GnssGeofenceInterface> GnssHal::getGnssGeofenceInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<hardware::gnss::IGnssGeofence> gnssGeofence; + auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence); + if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) { + return std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence); + } + } else if (gnssHal != nullptr) { + auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing(); + if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) { + return std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing); + } + } + return nullptr; +} + +std::unique_ptr<GnssBatchingInterface> GnssHal::getGnssBatchingInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<android::hardware::gnss::IGnssBatching> gnssBatchingAidl; + auto status = gnssHalAidl->getExtensionGnssBatching(&gnssBatchingAidl); + if (checkAidlStatus(status, "Unable to get a handle to GnssBatching interface.")) { + return std::make_unique<gnss::GnssBatching>(gnssBatchingAidl); + } + } + if (gnssHal_V2_0 != nullptr) { + auto gnssBatching_V2_0 = gnssHal_V2_0->getExtensionGnssBatching_2_0(); + if (checkHidlReturn(gnssBatching_V2_0, "Unable to get a handle to GnssBatching_V2_0")) { + return std::make_unique<gnss::GnssBatching_V2_0>(gnssBatching_V2_0); + } + } + if (gnssHal != nullptr) { + auto gnssBatching_V1_0 = gnssHal->getExtensionGnssBatching(); + if (checkHidlReturn(gnssBatching_V1_0, "Unable to get a handle to GnssBatching")) { + return std::make_unique<gnss::GnssBatching_V1_0>(gnssBatching_V1_0); + } + } + return nullptr; +} + +std::unique_ptr<MeasurementCorrectionsInterface> GnssHal::getMeasurementCorrectionsInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<android::hardware::gnss::measurement_corrections::IMeasurementCorrectionsInterface> + gnssMeasurementCorrectionsAidl; + auto status = + gnssHalAidl->getExtensionMeasurementCorrections(&gnssMeasurementCorrectionsAidl); + if (checkAidlStatus(status, + "Unable to get a handle to GnssVisibilityControl AIDL interface.")) { + return std::make_unique<gnss::MeasurementCorrectionsIface_Aidl>( + gnssMeasurementCorrectionsAidl); + } + } + if (gnssHal_V2_1 != nullptr) { + auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1(); + if (checkHidlReturn(gnssCorrections, + "Unable to get a handle to GnssMeasurementCorrections HIDL " + "interface")) { + return std::make_unique<gnss::MeasurementCorrectionsIface_V1_1>(gnssCorrections); + } + } + if (gnssHal_V2_0 != nullptr) { + auto gnssCorrections = gnssHal_V2_0->getExtensionMeasurementCorrections(); + if (checkHidlReturn(gnssCorrections, + "Unable to get a handle to GnssMeasurementCorrections HIDL " + "interface")) { + return std::make_unique<gnss::MeasurementCorrectionsIface_V1_0>(gnssCorrections); + } + } + return nullptr; +} + +std::unique_ptr<GnssVisibilityControlInterface> GnssHal::getGnssVisibilityControlInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<android::hardware::gnss::visibility_control::IGnssVisibilityControl> + gnssVisibilityControlAidl; + auto status = gnssHalAidl->getExtensionGnssVisibilityControl(&gnssVisibilityControlAidl); + if (checkAidlStatus(status, + "Unable to get a handle to GnssVisibilityControl AIDL interface.")) { + return std::make_unique<gnss::GnssVisibilityControlAidl>(gnssVisibilityControlAidl); + } + } else if (gnssHal_V2_0 != nullptr) { + auto gnssVisibilityControlHidl = gnssHal_V2_0->getExtensionVisibilityControl(); + if (checkHidlReturn(gnssVisibilityControlHidl, + "Unable to get a handle to GnssVisibilityControl HIDL interface")) { + return std::make_unique<gnss::GnssVisibilityControlHidl>(gnssVisibilityControlHidl); + } + } + return nullptr; +} + +std::unique_ptr<GnssAntennaInfoInterface> GnssHal::getGnssAntennaInfoInterface() { + if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { + sp<IGnssAntennaInfoAidl> gnssAntennaInfoAidl; + auto status = gnssHalAidl->getExtensionGnssAntennaInfo(&gnssAntennaInfoAidl); + if (checkAidlStatus(status, "Unable to get a handle to GnssAntennaInfo interface.")) { + return std::make_unique<gnss::GnssAntennaInfoAidl>(gnssAntennaInfoAidl); + } + } else if (gnssHal_V2_1 != nullptr) { + auto gnssAntennaInfo_V2_1 = gnssHal_V2_1->getExtensionGnssAntennaInfo(); + if (checkHidlReturn(gnssAntennaInfo_V2_1, + "Unable to get a handle to GnssAntennaInfo_V2_1")) { + return std::make_unique<gnss::GnssAntennaInfo_V2_1>(gnssAntennaInfo_V2_1); + } + } + return nullptr; +} + +std::unique_ptr<GnssPsdsInterface> GnssHal::getGnssPsdsInterface() { + if (gnssHalAidl != nullptr) { + sp<IGnssPsds> gnssPsdsAidl; + auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl); + if (checkAidlStatus(status, "Unable to get a handle to PSDS interface.")) { + return std::make_unique<gnss::GnssPsdsAidl>(gnssPsdsAidl); + } + } else if (gnssHal != nullptr) { + auto gnssXtra = gnssHal->getExtensionXtra(); + if (checkHidlReturn(gnssXtra, "Unable to get a handle to XTRA interface.")) { + return std::make_unique<gnss::GnssPsdsHidl>(gnssXtra); + } + } + return nullptr; +} + +sp<hardware::gnss::IGnssPowerIndication> GnssHal::getGnssPowerIndicationInterface() { + if (gnssHalAidl != nullptr) { + sp<hardware::gnss::IGnssPowerIndication> gnssPowerIndication; + auto status = gnssHalAidl->getExtensionGnssPowerIndication(&gnssPowerIndication); + if (checkAidlStatus(status, "Unable to get a handle to GnssPowerIndication")) { + return gnssPowerIndication; + } + } + return nullptr; +} + +sp<hardware::gnss::V1_0::IGnssNi> GnssHal::getGnssNiInterface() { + if (gnssHal != nullptr) { + auto gnssNi = gnssHal->getExtensionGnssNi(); + if (checkHidlReturn(gnssNi, "Unable to get a handle to GnssNi")) { + return gnssNi; + } + } + return nullptr; +} + +} // namespace android::gnss diff --git a/services/core/jni/gnss/Gnss.h b/services/core/jni/gnss/Gnss.h new file mode 100644 index 000000000000..c6743d62af45 --- /dev/null +++ b/services/core/jni/gnss/Gnss.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSS_H +#define _ANDROID_SERVER_GNSS_GNSS_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include <android/hardware/gnss/1.0/IGnss.h> +#include <android/hardware/gnss/1.1/IGnss.h> +#include <android/hardware/gnss/2.0/IGnss.h> +#include <android/hardware/gnss/2.1/IGnss.h> +#include <android/hardware/gnss/BnGnss.h> +#include <log/log.h> + +#include "AGnss.h" +#include "AGnssRil.h" +#include "GnssAntennaInfo.h" +#include "GnssBatching.h" +#include "GnssCallback.h" +#include "GnssConfiguration.h" +#include "GnssDebug.h" +#include "GnssGeofence.h" +#include "GnssMeasurement.h" +#include "GnssNavigationMessage.h" +#include "GnssPsds.h" +#include "GnssVisibilityControl.h" +#include "MeasurementCorrections.h" +#include "jni.h" + +namespace android::gnss { + +struct GnssDeathRecipient : virtual public hardware::hidl_death_recipient { + // hidl_death_recipient interface + virtual void serviceDied(uint64_t cookie, const wp<hidl::base::V1_0::IBase>& who) override { + ALOGE("IGNSS hidl service failed, trying to recover..."); + + JNIEnv* env = android::AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied); + } +}; + +class GnssHal { +public: + GnssHal(); + ~GnssHal() {} + + jboolean isSupported(); + jboolean setCallback(); + jboolean start(); + jboolean stop(); + jboolean setPositionMode(jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, + jint preferred_time, jboolean low_power_mode); + jboolean startSvStatus(); + jboolean stopSvStatus(); + jboolean startNmea(); + jboolean stopNmea(); + jint readNmea(jbyteArray& nmeaArray, jint& buffer_size); + void linkToDeath(); + void close(); + void deleteAidingData(jint flags); + void injectTime(jlong time, jlong timeReference, jint uncertainty); + void injectLocation(jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees, + jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, + jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos); + void injectBestLocation(jint gnssLocationFlags, jdouble latitudeDegrees, + jdouble longitudeDegrees, jdouble altitudeMeters, + jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, + jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos, + jdouble elapsedRealtimeUncertaintyNanos); + + std::unique_ptr<AGnssInterface> getAGnssInterface(); + std::unique_ptr<AGnssRilInterface> getAGnssRilInterface(); + std::unique_ptr<GnssNavigationMessageInterface> getGnssNavigationMessageInterface(); + std::unique_ptr<GnssMeasurementInterface> getGnssMeasurementInterface(); + std::unique_ptr<GnssDebugInterface> getGnssDebugInterface(); + std::unique_ptr<GnssConfigurationInterface> getGnssConfigurationInterface(); + std::unique_ptr<GnssGeofenceInterface> getGnssGeofenceInterface(); + std::unique_ptr<GnssBatchingInterface> getGnssBatchingInterface(); + std::unique_ptr<MeasurementCorrectionsInterface> getMeasurementCorrectionsInterface(); + std::unique_ptr<GnssVisibilityControlInterface> getGnssVisibilityControlInterface(); + std::unique_ptr<GnssAntennaInfoInterface> getGnssAntennaInfoInterface(); + std::unique_ptr<GnssPsdsInterface> getGnssPsdsInterface(); + + sp<hardware::gnss::IGnssPowerIndication> getGnssPowerIndicationInterface(); + sp<hardware::gnss::V1_0::IGnssNi> getGnssNiInterface(); + +private: + sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr; + sp<hardware::gnss::V1_0::IGnss> gnssHal = nullptr; + sp<hardware::gnss::V1_1::IGnss> gnssHal_V1_1 = nullptr; + sp<hardware::gnss::V2_0::IGnss> gnssHal_V2_0 = nullptr; + sp<hardware::gnss::V2_1::IGnss> gnssHal_V2_1 = nullptr; + sp<hardware::gnss::IGnss> gnssHalAidl = nullptr; +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_Gnss_H diff --git a/services/core/jni/gnss/GnssCallback.cpp b/services/core/jni/gnss/GnssCallback.cpp new file mode 100644 index 000000000000..b931e915a753 --- /dev/null +++ b/services/core/jni/gnss/GnssCallback.cpp @@ -0,0 +1,413 @@ +/* + * 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. + */ + +#define LOG_TAG "GnssCallbckJni" + +#include "GnssCallback.h" + +#include <hardware_legacy/power.h> + +#define WAKE_LOCK_NAME "GPS" + +namespace android::gnss { + +using android::hardware::gnss::V1_0::GnssLocationFlags; +using binder::Status; +using hardware::hidl_vec; +using hardware::Return; +using hardware::Void; + +using GnssLocationAidl = android::hardware::gnss::GnssLocation; +using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation; +using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation; +using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback; +using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback; +using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback; +using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback; + +jmethodID method_reportGnssServiceDied; + +namespace { + +jmethodID method_reportLocation; +jmethodID method_reportStatus; +jmethodID method_reportSvStatus; +jmethodID method_reportNmea; +jmethodID method_setTopHalCapabilities; +jmethodID method_setGnssYearOfHardware; +jmethodID method_setGnssHardwareModelName; +jmethodID method_requestLocation; +jmethodID method_requestUtcTime; + +// Returns true if location has lat/long information. +inline bool hasLatLong(const GnssLocationAidl& location) { + return (location.gnssLocationFlags & hardware::gnss::GnssLocation::HAS_LAT_LONG) != 0; +} + +// Returns true if location has lat/long information. +inline bool hasLatLong(const GnssLocation_V1_0& location) { + return (static_cast<uint32_t>(location.gnssLocationFlags) & GnssLocationFlags::HAS_LAT_LONG) != + 0; +} + +// Returns true if location has lat/long information. +inline bool hasLatLong(const GnssLocation_V2_0& location) { + return hasLatLong(location.v1_0); +} + +inline jboolean boolToJbool(bool value) { + return value ? JNI_TRUE : JNI_FALSE; +} + +// Must match the value from GnssMeasurement.java +const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4); + +} // anonymous namespace + +bool isSvStatusRegistered = false; +bool isNmeaRegistered = false; + +void Gnss_class_init_once(JNIEnv* env, jclass& clazz) { + method_reportLocation = + env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V"); + method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); + method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V"); + method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); + + method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V"); + method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V"); + method_setGnssHardwareModelName = + env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V"); + + method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V"); + method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V"); + method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V"); +} + +Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) { + ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities); + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) { + GnssCallbackHidl::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssLocationCb(const hardware::gnss::GnssLocation& location) { + GnssCallbackHidl::gnssLocationCbImpl<hardware::gnss::GnssLocation>(location); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) { + // In AIDL v1, if no listener is registered, do not report nmea to the framework. + if (getInterfaceVersion() <= 1) { + if (!isNmeaRegistered) { + return Status::ok(); + } + } + JNIEnv* env = getJniEnv(); + /* + * The Java code will call back to read these values. + * We do this to avoid creating unnecessary String objects. + */ + GnssCallbackHidl::sNmeaString = nmea.c_str(); + GnssCallbackHidl::sNmeaStringLength = nmea.size(); + + env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssAcquireWakelockCb() { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssReleaseWakelockCb() { + release_wake_lock(WAKE_LOCK_NAME); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) { + ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str()); + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); + jstring jstringName = env->NewStringUTF(info.name.c_str()); + env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); + if (jstringName) { + env->DeleteLocalRef(jstringName); + } + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssRequestTimeCb() { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss, + const bool isUserEmergency) { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), + boolToJbool(isUserEmergency)); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +// Implementation of IGnssCallbackHidl + +Return<void> GnssCallbackHidl::gnssNameCb(const android::hardware::hidl_string& name) { + ALOGD("%s: name=%s\n", __func__, name.c_str()); + + JNIEnv* env = getJniEnv(); + jstring jstringName = env->NewStringUTF(name.c_str()); + env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); + if (jstringName) { + env->DeleteLocalRef(jstringName); + } + checkAndClearExceptionFromCallback(env, __FUNCTION__); + + return Void(); +} + +const char* GnssCallbackHidl::sNmeaString = nullptr; +size_t GnssCallbackHidl::sNmeaStringLength = 0; + +template <class T> +Return<void> GnssCallbackHidl::gnssLocationCbImpl(const T& location) { + JNIEnv* env = getJniEnv(); + + jobject jLocation = translateGnssLocation(env, location); + + env->CallVoidMethod(mCallbacksObj, method_reportLocation, boolToJbool(hasLatLong(location)), + jLocation); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + env->DeleteLocalRef(jLocation); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssLocationCb(const GnssLocation_V1_0& location) { + return gnssLocationCbImpl<GnssLocation_V1_0>(location); +} + +Return<void> GnssCallbackHidl::gnssLocationCb_2_0(const GnssLocation_V2_0& location) { + return gnssLocationCbImpl<GnssLocation_V2_0>(location); +} + +Return<void> GnssCallbackHidl::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +template <> +uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag( + const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svStatus) { + return SVID_FLAGS_HAS_BASEBAND_CN0; +} + +template <> +uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag( + const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) { + return SVID_FLAGS_HAS_BASEBAND_CN0; +} + +template <> +double GnssCallbackHidl::getBasebandCn0DbHz( + const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) { + return svInfoList[i].basebandCN0DbHz; +} + +template <> +double GnssCallbackHidl::getBasebandCn0DbHz( + const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { + return svInfoList[i].basebandCN0DbHz; +} + +template <> +uint32_t GnssCallbackHidl::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) { + return svStatus.numSvs; +} + +template <> +uint32_t GnssCallbackHidl::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, + size_t i) { + return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation); +} + +template <> +uint32_t GnssCallbackHidl::getConstellationType( + const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { + return static_cast<uint32_t>(svInfoList[i].v2_0.constellation); +} + +template <class T_list, class T_sv_info> +Return<void> GnssCallbackHidl::gnssSvStatusCbImpl(const T_list& svStatus) { + // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework. + if (!isSvStatusRegistered) { + return Void(); + } + + JNIEnv* env = getJniEnv(); + + uint32_t listSize = getGnssSvInfoListSize(svStatus); + + jintArray svidWithFlagArray = env->NewIntArray(listSize); + jfloatArray cn0Array = env->NewFloatArray(listSize); + jfloatArray elevArray = env->NewFloatArray(listSize); + jfloatArray azimArray = env->NewFloatArray(listSize); + jfloatArray carrierFreqArray = env->NewFloatArray(listSize); + jfloatArray basebandCn0Array = env->NewFloatArray(listSize); + + jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0); + jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0); + jfloat* elev = env->GetFloatArrayElements(elevArray, 0); + jfloat* azim = env->GetFloatArrayElements(azimArray, 0); + jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0); + jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0); + + /* + * Read GNSS SV info. + */ + for (size_t i = 0; i < listSize; ++i) { + enum ShiftWidth : uint8_t { SVID_SHIFT_WIDTH = 12, CONSTELLATION_TYPE_SHIFT_WIDTH = 8 }; + + const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i); + svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) | + (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) | + static_cast<uint32_t>(info.svFlag); + cn0s[i] = info.cN0Dbhz; + elev[i] = info.elevationDegrees; + azim[i] = info.azimuthDegrees; + carrierFreq[i] = info.carrierFrequencyHz; + svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus); + basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i); + } + + env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0); + env->ReleaseFloatArrayElements(cn0Array, cn0s, 0); + env->ReleaseFloatArrayElements(elevArray, elev, 0); + env->ReleaseFloatArrayElements(azimArray, azim, 0); + env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0); + env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0); + + env->CallVoidMethod(mCallbacksObj, method_reportSvStatus, static_cast<jint>(listSize), + svidWithFlagArray, cn0Array, elevArray, azimArray, carrierFreqArray, + basebandCn0Array); + + env->DeleteLocalRef(svidWithFlagArray); + env->DeleteLocalRef(cn0Array); + env->DeleteLocalRef(elevArray); + env->DeleteLocalRef(azimArray); + env->DeleteLocalRef(carrierFreqArray); + env->DeleteLocalRef(basebandCn0Array); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssNmeaCb(int64_t timestamp, + const ::android::hardware::hidl_string& nmea) { + // In HIDL, if no listener is registered, do not report nmea to the framework. + if (!isNmeaRegistered) { + return Void(); + } + JNIEnv* env = getJniEnv(); + /* + * The Java code will call back to read these values. + * We do this to avoid creating unnecessary String objects. + */ + sNmeaString = nmea.c_str(); + sNmeaStringLength = nmea.size(); + + env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssSetCapabilitesCb(uint32_t capabilities) { + ALOGD("%s: %du\n", __func__, capabilities); + + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) { + return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities); +} + +Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) { + return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities); +} + +Return<void> GnssCallbackHidl::gnssAcquireWakelockCb() { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssReleaseWakelockCb() { + release_wake_lock(WAKE_LOCK_NAME); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssRequestTimeCb() { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssRequestLocationCb(const bool independentFromGnss) { + return GnssCallbackHidl::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */ + false); +} + +Return<void> GnssCallbackHidl::gnssRequestLocationCb_2_0(const bool independentFromGnss, + const bool isUserEmergency) { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), + boolToJbool(isUserEmergency)); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +Return<void> GnssCallbackHidl::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) { + ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw); + + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +} // namespace android::gnss
\ No newline at end of file diff --git a/services/core/jni/gnss/GnssCallback.h b/services/core/jni/gnss/GnssCallback.h new file mode 100644 index 000000000000..a7f96fb0f5c7 --- /dev/null +++ b/services/core/jni/gnss/GnssCallback.h @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSCALLBACK_H +#define _ANDROID_SERVER_GNSS_GNSSCALLBACK_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include <android/hardware/gnss/2.1/IGnss.h> +#include <android/hardware/gnss/BnGnssCallback.h> +#include <log/log.h> + +#include <vector> + +#include "Utils.h" +#include "jni.h" + +namespace android::gnss { + +namespace { + +extern jmethodID method_reportLocation; +extern jmethodID method_reportStatus; +extern jmethodID method_reportSvStatus; +extern jmethodID method_reportNmea; +extern jmethodID method_setTopHalCapabilities; +extern jmethodID method_setGnssYearOfHardware; +extern jmethodID method_setGnssHardwareModelName; +extern jmethodID method_requestLocation; +extern jmethodID method_requestUtcTime; + +} // anonymous namespace + +extern bool isSvStatusRegistered; +extern bool isNmeaRegistered; + +extern jmethodID method_reportGnssServiceDied; + +void Gnss_class_init_once(JNIEnv* env, jclass& clazz); + +/* + * GnssCallbackAidl class implements the callback methods for AIDL IGnssCallback interface. + */ +class GnssCallbackAidl : public hardware::gnss::BnGnssCallback { +public: + binder::Status gnssSetCapabilitiesCb(const int capabilities) override; + binder::Status gnssStatusCb(const GnssStatusValue status) override; + binder::Status gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) override; + binder::Status gnssLocationCb(const hardware::gnss::GnssLocation& location) override; + binder::Status gnssNmeaCb(const int64_t timestamp, const std::string& nmea) override; + binder::Status gnssAcquireWakelockCb() override; + binder::Status gnssReleaseWakelockCb() override; + binder::Status gnssSetSystemInfoCb(const GnssSystemInfo& info) override; + binder::Status gnssRequestTimeCb() override; + binder::Status gnssRequestLocationCb(const bool independentFromGnss, + const bool isUserEmergency) override; +}; + +/* + * GnssCallbackHidl class implements the callback methods for HIDL IGnssCallback interface. + */ +struct GnssCallbackHidl : public hardware::gnss::V2_1::IGnssCallback { + hardware::Return<void> gnssLocationCb( + const hardware::gnss::V1_0::GnssLocation& location) override; + hardware::Return<void> gnssStatusCb( + const hardware::gnss::V1_0::IGnssCallback::GnssStatusValue status) override; + hardware::Return<void> gnssSvStatusCb( + const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus) override { + return gnssSvStatusCbImpl<hardware::gnss::V1_0::IGnssCallback::GnssSvStatus, + hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svStatus); + } + hardware::Return<void> gnssNmeaCb(int64_t timestamp, + const hardware::hidl_string& nmea) override; + hardware::Return<void> gnssSetCapabilitesCb(uint32_t capabilities) override; + hardware::Return<void> gnssAcquireWakelockCb() override; + hardware::Return<void> gnssReleaseWakelockCb() override; + hardware::Return<void> gnssRequestTimeCb() override; + hardware::Return<void> gnssRequestLocationCb(const bool independentFromGnss) override; + + hardware::Return<void> gnssSetSystemInfoCb( + const hardware::gnss::V1_0::IGnssCallback::GnssSystemInfo& info) override; + + // New in 1.1 + hardware::Return<void> gnssNameCb(const hardware::hidl_string& name) override; + + // New in 2.0 + hardware::Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss, + const bool isUserEmergency) override; + hardware::Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override; + hardware::Return<void> gnssLocationCb_2_0( + const hardware::gnss::V2_0::GnssLocation& location) override; + hardware::Return<void> gnssSvStatusCb_2_0( + const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList) + override { + return gnssSvStatusCbImpl< + hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>, + hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList); + } + + // New in 2.1 + hardware::Return<void> gnssSvStatusCb_2_1( + const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList) + override { + return gnssSvStatusCbImpl< + hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>, + hardware::gnss::V1_0::IGnssCallback::GnssSvInfo>(svInfoList); + } + hardware::Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override; + + // TODO: Reconsider allocation cost vs threadsafety on these statics + static const char* sNmeaString; + static size_t sNmeaStringLength; + + template <class T> + static hardware::Return<void> gnssLocationCbImpl(const T& location); + + template <class T_list, class T_sv_info> + static hardware::Return<void> gnssSvStatusCbImpl(const T_list& svStatus); + +private: + template <class T> + static uint32_t getHasBasebandCn0DbHzFlag(const T& svStatus) { + return 0; + } + + template <class T> + static double getBasebandCn0DbHz(const T& svStatus, size_t i) { + return 0.0; + } + + template <class T> + static uint32_t getGnssSvInfoListSize(const T& svInfoList) { + return svInfoList.size(); + } + + static const hardware::gnss::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex( + const std::vector<hardware::gnss::IGnssCallback::GnssSvInfo>& svInfoList, size_t i) { + return svInfoList[i]; + } + + static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex( + const hardware::gnss::V1_0::IGnssCallback::GnssSvStatus& svStatus, size_t i) { + return svStatus.gnssSvList.data()[i]; + } + + static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex( + const hardware::hidl_vec<hardware::gnss::V2_0::IGnssCallback::GnssSvInfo>& svInfoList, + size_t i) { + return svInfoList[i].v1_0; + } + + static const hardware::gnss::V1_0::IGnssCallback::GnssSvInfo& getGnssSvInfoOfIndex( + const hardware::hidl_vec<hardware::gnss::V2_1::IGnssCallback::GnssSvInfo>& svInfoList, + size_t i) { + return svInfoList[i].v2_0.v1_0; + } + + template <class T> + static uint32_t getConstellationType(const T& svInfoList, size_t i) { + return static_cast<uint32_t>(svInfoList[i].constellation); + } +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSCALLBACK_H
\ No newline at end of file diff --git a/services/core/jni/gnss/GnssDebug.cpp b/services/core/jni/gnss/GnssDebug.cpp index da5331760e5c..263a6c635f28 100644 --- a/services/core/jni/gnss/GnssDebug.cpp +++ b/services/core/jni/gnss/GnssDebug.cpp @@ -104,4 +104,10 @@ const android::hardware::gnss::IGnssDebug::SatelliteData& GnssDebugUtil::getSate return satelliteDataArray[i]; } +template <> +int64_t GnssDebugUtil::getTimeEstimateMs( + const android::hardware::gnss::IGnssDebug::DebugData& data) { + return data.time.timeEstimateMs; +} + } // namespace android::gnss diff --git a/services/core/jni/gnss/GnssDebug.h b/services/core/jni/gnss/GnssDebug.h index 1e1a7b4c1abd..e02c2ac838e7 100644 --- a/services/core/jni/gnss/GnssDebug.h +++ b/services/core/jni/gnss/GnssDebug.h @@ -113,12 +113,6 @@ int64_t GnssDebugUtil::getTimeEstimateMs(const T& data) { return data.time.timeEstimate; } -template <> -int64_t GnssDebugUtil::getTimeEstimateMs( - const android::hardware::gnss::IGnssDebug::DebugData& data) { - return data.time.timeEstimateMs; -} - template <class T_DebugData, class T_SatelliteData> jstring GnssDebugUtil::parseDebugData(JNIEnv* env, std::stringstream& internalState, const T_DebugData& data) { diff --git a/services/core/jni/gnss/GnssPsds.cpp b/services/core/jni/gnss/GnssPsds.cpp new file mode 100644 index 000000000000..51a1450c0303 --- /dev/null +++ b/services/core/jni/gnss/GnssPsds.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +// Define LOG_TAG before <log/log.h> to overwrite the default value. +#define LOG_TAG "GnssPsdsJni" + +#include "GnssPsds.h" + +#include "Utils.h" + +using android::hardware::hidl_bitfield; +using android::hardware::gnss::PsdsType; +using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds; +using IGnssPsdsHidl = android::hardware::gnss::V1_0::IGnssXtra; + +namespace android::gnss { + +// Implementation of GnssPsds (AIDL HAL) + +GnssPsdsAidl::GnssPsdsAidl(const sp<IGnssPsdsAidl>& iGnssPsds) : mIGnssPsds(iGnssPsds) { + assert(mIGnssPsds != nullptr); +} + +jboolean GnssPsdsAidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) { + auto status = mIGnssPsds->setCallback(callback->getAidl()); + return checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed."); +} + +void GnssPsdsAidl::injectPsdsData(const jbyteArray& data, const jint& length, + const jint& psdsType) { + JNIEnv* env = getJniEnv(); + jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0)); + auto status = mIGnssPsds->injectPsdsData(static_cast<PsdsType>(psdsType), + std::vector<uint8_t>((const uint8_t*)bytes, + (const uint8_t*)bytes + length)); + checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed."); + env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); +} + +// Implementation of GnssPsdsHidl + +GnssPsdsHidl::GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra) + : mIGnssXtra(iGnssXtra) { + assert(mIGnssXtra != nullptr); +} + +jboolean GnssPsdsHidl::setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) { + auto result = mIGnssXtra->setCallback(callback->getHidl()); + return checkHidlReturn(result, "IGnssPsdsHidl setCallback() failed."); +} + +void GnssPsdsHidl::injectPsdsData(const jbyteArray& data, const jint& length, const jint&) { + JNIEnv* env = getJniEnv(); + jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(data, 0)); + auto result = mIGnssXtra->injectXtraData(std::string((const char*)bytes, length)); + checkHidlReturn(result, "IGnssXtra injectXtraData() failed."); + env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); +} + +} // namespace android::gnss diff --git a/services/core/jni/gnss/GnssPsds.h b/services/core/jni/gnss/GnssPsds.h new file mode 100644 index 000000000000..6b65ee8614f8 --- /dev/null +++ b/services/core/jni/gnss/GnssPsds.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSPSDS_H +#define _ANDROID_SERVER_GNSS_GNSSPSDS_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include <android/hardware/gnss/1.0/IGnssXtra.h> +#include <android/hardware/gnss/BnGnssPsds.h> +#include <log/log.h> + +#include "GnssPsdsCallback.h" +#include "jni.h" + +namespace android::gnss { + +class GnssPsdsInterface { +public: + virtual ~GnssPsdsInterface() {} + virtual jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback); + virtual void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType); +}; + +class GnssPsdsAidl : public GnssPsdsInterface { +public: + GnssPsdsAidl(const sp<android::hardware::gnss::IGnssPsds>& iGnssPsds); + jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override; + void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override; + +private: + const sp<android::hardware::gnss::IGnssPsds> mIGnssPsds; +}; + +class GnssPsdsHidl : public GnssPsdsInterface { +public: + GnssPsdsHidl(const sp<android::hardware::gnss::V1_0::IGnssXtra>& iGnssXtra); + jboolean setCallback(const std::unique_ptr<GnssPsdsCallback>& callback) override; + void injectPsdsData(const jbyteArray& data, const jint& length, const jint& psdsType) override; + +private: + const sp<android::hardware::gnss::V1_0::IGnssXtra> mIGnssXtra; +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSPSDS_H diff --git a/services/core/jni/gnss/GnssPsdsCallback.cpp b/services/core/jni/gnss/GnssPsdsCallback.cpp new file mode 100644 index 000000000000..1dd7022c66ee --- /dev/null +++ b/services/core/jni/gnss/GnssPsdsCallback.cpp @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#define LOG_TAG "GnssPsdsCbJni" + +#include "GnssPsdsCallback.h" + +#include <vector> + +#include "Utils.h" + +namespace android::gnss { + +namespace { +jmethodID method_psdsDownloadRequest; +} // anonymous namespace + +using binder::Status; +using hardware::Return; +using hardware::Void; +using hardware::gnss::PsdsType; + +void GnssPsds_class_init_once(JNIEnv* env, jclass clazz) { + method_psdsDownloadRequest = env->GetMethodID(clazz, "psdsDownloadRequest", "(I)V"); +} + +// Implementation of android::hardware::gnss::IGnssPsdsCallback + +Status GnssPsdsCallbackAidl::downloadRequestCb(PsdsType psdsType) { + ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType)); + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Status::ok(); +} + +// Implementation of android::hardware::gnss::V1_0::IGnssPsdsCallback + +Return<void> GnssPsdsCallbackHidl::downloadRequestCb() { + JNIEnv* env = getJniEnv(); + env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return Void(); +} + +} // namespace android::gnss diff --git a/services/core/jni/gnss/GnssPsdsCallback.h b/services/core/jni/gnss/GnssPsdsCallback.h new file mode 100644 index 000000000000..3ac7473c42a9 --- /dev/null +++ b/services/core/jni/gnss/GnssPsdsCallback.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H +#define _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include <android/hardware/gnss/1.0/IGnssXtraCallback.h> +#include <android/hardware/gnss/BnGnssPsdsCallback.h> +#include <log/log.h> + +#include "jni.h" + +namespace android::gnss { + +namespace { +extern jmethodID method_psdsDownloadRequest; +} // anonymous namespace + +void GnssPsds_class_init_once(JNIEnv* env, jclass clazz); + +class GnssPsdsCallbackAidl : public hardware::gnss::BnGnssPsdsCallback { +public: + GnssPsdsCallbackAidl() {} + binder::Status downloadRequestCb(hardware::gnss::PsdsType psdsType) override; +}; + +class GnssPsdsCallbackHidl : public hardware::gnss::V1_0::IGnssXtraCallback { +public: + GnssPsdsCallbackHidl() {} + hardware::Return<void> downloadRequestCb() override; +}; + +class GnssPsdsCallback { +public: + GnssPsdsCallback() {} + sp<GnssPsdsCallbackAidl> getAidl() { + if (callbackAidl == nullptr) { + callbackAidl = sp<GnssPsdsCallbackAidl>::make(); + } + return callbackAidl; + } + + sp<GnssPsdsCallbackHidl> getHidl() { + if (callbackHidl == nullptr) { + callbackHidl = sp<GnssPsdsCallbackHidl>::make(); + } + return callbackHidl; + } + +private: + sp<GnssPsdsCallbackAidl> callbackAidl; + sp<GnssPsdsCallbackHidl> callbackHidl; +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSPSDSCALLBACK_H
\ No newline at end of file diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 33e97aa5eaf2..5aa3dfea3ea4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1999,9 +1999,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.load(); setDeviceOwnershipSystemPropertyLocked(); findOwnerComponentIfNecessaryLocked(); - - // TODO PO may not have a class name either due to b/17652534. Address that too. - updateDeviceOwnerLocked(); } } @@ -3142,23 +3139,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void updateDeviceOwnerLocked() { - long ident = mInjector.binderClearCallingIdentity(); - try { - // TODO This is to prevent DO from getting "clear data"ed, but it should also check the - // user id and also protect all other DAs too. - final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent(); - if (deviceOwnerComponent != null) { - mInjector.getIActivityManager() - .updateDeviceOwner(deviceOwnerComponent.getPackageName()); - } - } catch (RemoteException e) { - // Not gonna happen. - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } - } - static void validateQualityConstant(int quality) { switch (quality) { case PASSWORD_QUALITY_UNSPECIFIED: @@ -8590,7 +8570,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.setDeviceOwner(admin, ownerName, userId); mOwners.writeDeviceOwner(); - updateDeviceOwnerLocked(); setDeviceOwnershipSystemPropertyLocked(); //TODO(b/180371154): when provisionFullyManagedDevice is used in tests, remove this @@ -8951,7 +8930,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mOwners.clearDeviceOwner(); mOwners.writeDeviceOwner(); - updateDeviceOwnerLocked(); clearDeviceOwnerUserRestriction(UserHandle.of(userId)); mInjector.securityLogSetLoggingEnabledProperty(false); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e895d377f607..5098abe4bb55 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -386,8 +386,8 @@ public final class SystemServer implements Dumpable { "com.android.server.DeviceIdleController"; private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = "com.android.server.blob.BlobStoreManagerService"; - private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = - "com.android.server.appsearch.AppSearchManagerService"; + private static final String APPSEARCH_MODULE_LIFECYCLE_CLASS = + "com.android.server.appsearch.AppSearchModule$Lifecycle"; private static final String ISOLATED_COMPILATION_SERVICE_CLASS = "com.android.server.compos.IsolatedCompilationService"; private static final String ROLLBACK_MANAGER_SERVICE_CLASS = @@ -2764,8 +2764,8 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS); t.traceEnd(); - t.traceBegin("AppSearchManagerService"); - mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); + t.traceBegin("AppSearchModule"); + mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS); t.traceEnd(); if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index d2358a08624d..023608c68ee3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -1167,7 +1167,14 @@ public class GameManagerServiceTests { startUser(gameManagerService, USER_ID_1); gameManagerService.setGameMode( mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1); - GameState gameState = new GameState(isLoading, GameState.MODE_NONE); + int testMode = GameState.MODE_NONE; + int testLabel = 99; + int testQuality = 123; + GameState gameState = new GameState(isLoading, testMode, testLabel, testQuality); + assertEquals(isLoading, gameState.isLoading()); + assertEquals(testMode, gameState.getMode()); + assertEquals(testLabel, gameState.getLabel()); + assertEquals(testQuality, gameState.getQuality()); gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); mTestLooper.dispatchAll(); verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 2398e367ebfe..b601d14f1f58 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1137,9 +1137,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; // Verify internal calls. - verify(getServices().iactivityManager, times(1)).updateDeviceOwner( - eq(admin1.getPackageName())); - verify(getServices().userManager, times(1)).setUserRestriction( eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE), eq(true), eq(UserHandle.SYSTEM)); @@ -1205,9 +1202,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); // Verify internal calls. - verify(getServices().iactivityManager).updateDeviceOwner( - eq(admin1.getPackageName())); - verify(mContext.spiedContext).sendBroadcastAsUser( MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED), MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)); @@ -1392,11 +1386,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue(); - - // Verify internal calls. - verify(getServices().iactivityManager, times(1)).updateDeviceOwner( - eq(admin1.getPackageName())); - assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); dpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER); @@ -1501,11 +1490,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); dpm.setActiveAdmin(admin1, /* replace =*/ false); assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue(); - - // Verify internal calls. - verify(getServices().iactivityManager, times(1)).updateDeviceOwner( - eq(admin1.getPackageName())); - assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isEqualTo(admin1); // Now call clear from the secondary user, which should throw. diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index ab21ab05ab5f..03363a100841 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -71,9 +71,11 @@ public class RuleBinaryParserTest { private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS); private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS); + private static final String APP_CERTIFICATE_LINEAGE = + getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS); private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS); private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS); - private static final int INVALID_KEY_VALUE = 8; + private static final int INVALID_KEY_VALUE = 9; private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS); private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS); @@ -337,6 +339,40 @@ public class RuleBinaryParserTest { } @Test + public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception { + String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + String ruleBits = + START_BIT + + ATOMIC_FORMULA_START_BITS + + APP_CERTIFICATE_LINEAGE + + EQ + + IS_HASHED + + getBits(appCertificate.length(), VALUE_SIZE_BITS) + + getValueBits(appCertificate) + + DENY + + END_BIT; + byte[] ruleBytes = getBytes(ruleBits); + ByteBuffer rule = + ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); + rule.put(DEFAULT_FORMAT_VERSION_BYTES); + rule.put(ruleBytes); + + RuleParser binaryParser = new RuleBinaryParser(); + Rule expectedRule = + new Rule( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE_LINEAGE, + IntegrityUtils.getHexDigest( + appCertificate.getBytes(StandardCharsets.UTF_8)), + /* isHashedValue= */ true), + Rule.DENY); + + List<Rule> rules = binaryParser.parse(rule.array()); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception { int versionCode = 1; String ruleBits = diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java index c77100045118..0287510041be 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java @@ -41,7 +41,9 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; +import android.os.HandlerThread; import android.os.LocaleList; +import android.os.Process; import android.os.RemoteException; import android.os.SimpleClock; import android.util.SparseArray; @@ -78,6 +80,7 @@ import java.util.Map; */ @RunWith(AndroidJUnit4.class) public class LocaleManagerBackupRestoreTest { + private static final String TAG = "LocaleManagerBackupRestoreTest"; private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp"; private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB"; private static final String TEST_LOCALES_XML_TAG = "locales"; @@ -131,12 +134,17 @@ public class LocaleManagerBackupRestoreTest { doReturn(mMockPackageManager).when(mMockContext).getPackageManager(); + HandlerThread broadcastHandlerThread = new HandlerThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND); + broadcastHandlerThread.start(); + mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext, - mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA)); + mMockLocaleManagerService, mMockPackageManagerInternal, mClock, STAGE_DATA, + broadcastHandlerThread)); doNothing().when(mBackupHelper).notifyBackupManager(); mUserMonitor = mBackupHelper.getUserMonitor(); - mPackageMonitor = mBackupHelper.getPackageMonitor(); + mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper); setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS); } diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java index ca5b0cb1112f..0b3ef4542cbf 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java @@ -46,6 +46,7 @@ import android.os.LocaleList; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.internal.content.PackageMonitor; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.PackageConfig; @@ -86,6 +87,8 @@ public class LocaleManagerServiceTest { private ActivityTaskManagerInternal mMockActivityTaskManager; @Mock private ActivityManagerInternal mMockActivityManager; + @Mock + PackageMonitor mMockPackageMonitor; @Before public void setUp() throws Exception { @@ -93,6 +96,7 @@ public class LocaleManagerServiceTest { mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class); mMockActivityManager = mock(ActivityManagerInternal.class); mMockPackageManagerInternal = mock(PackageManagerInternal.class); + mMockPackageMonitor = mock(PackageMonitor.class); // For unit tests, set the default installer info PackageManager mockPackageManager = mock(PackageManager.class); @@ -113,7 +117,8 @@ public class LocaleManagerServiceTest { mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class); mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager, - mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper); + mMockActivityManager, mMockPackageManagerInternal, + mMockBackupHelper, mMockPackageMonitor); } @Test(expected = SecurityException.class) diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java index b0fc6363b701..ad9be0d5fe8d 100644 --- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java +++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java @@ -18,6 +18,7 @@ package com.android.server.locales; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.os.HandlerThread; import android.util.SparseArray; import java.time.Clock; @@ -31,7 +32,8 @@ public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper { ShadowLocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, PackageManagerInternal pmInternal, Clock clock, - SparseArray<LocaleManagerBackupHelper.StagedData> stagedData) { - super(context, localeManagerService, pmInternal, clock, stagedData); + SparseArray<LocaleManagerBackupHelper.StagedData> stagedData, + HandlerThread broadcastHandlerThread) { + super(context, localeManagerService, pmInternal, clock, stagedData, broadcastHandlerThread); } } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java index 16cfd13b18c2..6bdd88c6a712 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -49,7 +48,6 @@ import android.os.IBinder; import android.os.IHwBinder; import android.os.IHwInterface; import android.os.RemoteException; -import android.system.OsConstants; import org.junit.After; import org.junit.Before; @@ -60,6 +58,7 @@ import org.mockito.ArgumentCaptor; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; @RunWith(Parameterized.class) public class SoundHw2CompatTest { @@ -240,14 +239,15 @@ public class SoundHw2CompatTest { (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; final int handle = 29; - ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor = - ArgumentCaptor.forClass( - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class); + AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model = + new AtomicReference<>(); ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class); doAnswer(invocation -> { + // We need to dup the model, as it gets invalidated after the call returns. + model.set(TestUtil.dupModel_2_1(invocation.getArgument(0))); android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback resultCallback = invocation.getArgument(3); @@ -259,10 +259,9 @@ public class SoundHw2CompatTest { assertEquals(handle, mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback)); - verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(), - anyInt(), any()); + verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any()); - TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue()); + TestUtil.validateGenericSoundModel_2_1(model.get()); validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback); return handle; } @@ -355,14 +354,16 @@ public class SoundHw2CompatTest { (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver; final int handle = 29; - ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> - modelCaptor = ArgumentCaptor.forClass( - android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class); + AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model = + new AtomicReference<>(); ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor = ArgumentCaptor.forClass( android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class); doAnswer(invocation -> { + // We need to dup the model, as it gets invalidated after the call returns. + model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0))); + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback resultCallback = invocation.getArgument(3); @@ -374,10 +375,10 @@ public class SoundHw2CompatTest { assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback)); - verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(), - anyInt(), any()); + verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), + any()); - TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue()); + TestUtil.validatePhraseSoundModel_2_1(model.get()); validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback); return handle; } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java index e687a2adeb39..30b4a59b32b1 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java @@ -47,6 +47,7 @@ import android.os.HidlMemoryUtil; import android.os.ParcelFileDescriptor; import android.os.SharedMemory; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; @@ -82,6 +83,19 @@ class TestUtil { HidlMemoryUtil.hidlMemoryToByteArray(model.data)); } + static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1( + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) { + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup = + new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel(); + dup.header = model.header; + try { + dup.data = model.data.dup(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return dup; + } + private static void validateSoundModel_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) { assertEquals(type, model.type); @@ -121,6 +135,15 @@ class TestUtil { validatePhrases_2_0(model.phrases); } + static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1( + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) { + android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup = + new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel(); + dup.common = dupModel_2_1(model.common); + dup.phrases = model.phrases; + return dup; + } + static void validatePhraseSoundModel_2_0( android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) { validateSoundModel_2_0(model.common, @@ -190,31 +213,27 @@ class TestUtil { properties.maxKeyPhrases = 567; properties.maxUsers = 678; properties.recognitionModes = - RecognitionMode.VOICE_TRIGGER - | RecognitionMode.USER_IDENTIFICATION - | RecognitionMode.USER_AUTHENTICATION - | RecognitionMode.GENERIC_TRIGGER; + RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION + | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER; properties.captureTransition = true; properties.maxBufferMs = 321; properties.concurrentCapture = supportConcurrentCapture; properties.triggerInEvent = true; properties.powerConsumptionMw = 432; properties.supportedModelArch = "supportedModelArch"; - properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION - | AudioCapabilities.NOISE_SUPPRESSION; + properties.audioCapabilities = + AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION; return properties; } - static void validateDefaultProperties(Properties properties, - boolean supportConcurrentCapture) { + static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) { validateDefaultProperties(properties, supportConcurrentCapture, AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION, "supportedModelArch"); } - static void validateDefaultProperties(Properties properties, - boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities, - @NonNull String supportedModelArch) { + static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture, + @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) { assertEquals("implementor", properties.implementor); assertEquals("description", properties.description); assertEquals(123, properties.version); @@ -222,10 +241,9 @@ class TestUtil { assertEquals(456, properties.maxSoundModels); assertEquals(567, properties.maxKeyPhrases); assertEquals(678, properties.maxUsers); - assertEquals(RecognitionMode.GENERIC_TRIGGER - | RecognitionMode.USER_AUTHENTICATION - | RecognitionMode.USER_IDENTIFICATION - | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes); + assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION + | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER, + properties.recognitionModes); assertTrue(properties.captureTransition); assertEquals(321, properties.maxBufferMs); assertEquals(supportConcurrentCapture, properties.concurrentCapture); @@ -246,8 +264,8 @@ class TestUtil { config.phraseRecognitionExtras[0].levels[0].userId = 234; config.phraseRecognitionExtras[0].levels[0].levelPercent = 34; config.data = new byte[]{5, 4, 3, 2, 1}; - config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION - | AudioCapabilities.NOISE_SUPPRESSION; + config.audioCapabilities = + AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION; return config; } @@ -295,13 +313,12 @@ class TestUtil { int captureHandle) { validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle); - assertEquals(AudioCapabilities.ECHO_CANCELLATION - | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities); + assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION, + config.audioCapabilities); } static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0( - int hwHandle, - int status) { + int hwHandle, int status) { android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent = new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent(); halEvent.status = status; @@ -351,8 +368,7 @@ class TestUtil { return event; } - static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1( - int hwHandle, + static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle, int status) { ISoundTriggerHwCallback.RecognitionEvent halEvent = new ISoundTriggerHwCallback.RecognitionEvent(); @@ -386,8 +402,7 @@ class TestUtil { PhraseRecognitionExtra extra = new PhraseRecognitionExtra(); extra.id = 123; extra.confidenceLevel = 52; - extra.recognitionModes = RecognitionMode.VOICE_TRIGGER - | RecognitionMode.GENERIC_TRIGGER; + extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER; ConfidenceLevel level = new ConfidenceLevel(); level.userId = 31; level.levelPercent = 43; @@ -396,8 +411,8 @@ class TestUtil { return event; } - static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent - createPhraseRecognitionEvent_2_0(int hwHandle, int status) { + static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0( + int hwHandle, int status) { android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent = new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent(); halEvent.common = createRecognitionEvent_2_0(hwHandle, status); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 01e306e744fb..3d24a814c7cd 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -113,6 +113,9 @@ public class VibrationThreadTest { private TestLooper mTestLooper; private TestLooperAutoDispatcher mCustomTestLooperDispatcher; + // Setup from the providers when VibrationThread is initialized. + private SparseArray<VibratorController> mControllers; + @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); @@ -178,7 +181,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -197,7 +200,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -219,7 +222,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(15)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -243,7 +246,7 @@ public class VibrationThreadTest { thread, TEST_TIMEOUT_MILLIS)); // Vibration still running after 2 cycles. assertTrue(thread.isAlive()); - assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); thread.cancel(); waitForCompletion(thread); @@ -251,7 +254,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong()); verify(mManagerHooks).noteVibratorOff(eq(UID)); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); List<Float> playedAmplitudes = fakeVibrator.getAmplitudes(); assertFalse(fakeVibrator.getEffectSegments().isEmpty()); @@ -280,7 +283,7 @@ public class VibrationThreadTest { waitForCompletion(thread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(1000)), fakeVibrator.getEffectSegments()); } @@ -302,7 +305,7 @@ public class VibrationThreadTest { waitForCompletion(thread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(5550)), fakeVibrator.getEffectSegments()); } @@ -325,7 +328,7 @@ public class VibrationThreadTest { waitForCompletion(thread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(2, fakeVibrator.getEffectSegments().size()); // First time turn vibrator ON for minimum of 1s. assertEquals(1000L, fakeVibrator.getEffectSegments().get(0).getDuration()); @@ -350,7 +353,7 @@ public class VibrationThreadTest { .compose(); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread, + assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread, TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); @@ -363,7 +366,7 @@ public class VibrationThreadTest { waitForCompletion(cancellingThread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @Test @@ -375,7 +378,7 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread, + assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), vibrationThread, TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); @@ -388,7 +391,7 @@ public class VibrationThreadTest { waitForCompletion(cancellingThread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @Test @@ -404,7 +407,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -427,7 +430,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -466,7 +469,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0), expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)), @@ -536,7 +539,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedOneShot(10), expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0), @@ -573,7 +576,7 @@ public class VibrationThreadTest { verify(mManagerHooks).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10), expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0, @@ -626,11 +629,11 @@ public class VibrationThreadTest { TEST_TIMEOUT_MILLIS)); // Vibration still running after 2 cycles. assertTrue(thread.isAlive()); - assertTrue(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertTrue(mControllers.get(VIBRATOR_ID).isVibrating()); thread.binderDied(); waitForCompletion(thread); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); } @@ -667,7 +670,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); @@ -692,9 +695,9 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(1).isVibrating()); - assertFalse(thread.getVibrators().get(2).isVibrating()); - assertFalse(thread.getVibrators().get(3).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); + assertFalse(mControllers.get(3).isVibrating()); VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK); assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments()); @@ -731,10 +734,10 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(1).isVibrating()); - assertFalse(thread.getVibrators().get(2).isVibrating()); - assertFalse(thread.getVibrators().get(3).isVibrating()); - assertFalse(thread.getVibrators().get(4).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); + assertFalse(mControllers.get(3).isVibrating()); + assertFalse(mControllers.get(4).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), mVibratorProviders.get(1).getEffectSegments()); @@ -782,9 +785,9 @@ public class VibrationThreadTest { batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(1).isVibrating()); - assertFalse(thread.getVibrators().get(2).isVibrating()); - assertFalse(thread.getVibrators().get(3).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); + assertFalse(mControllers.get(3).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), mVibratorProviders.get(1).getEffectSegments()); @@ -941,9 +944,9 @@ public class VibrationThreadTest { // All vibrators are turned on in parallel. assertTrue(waitUntil( - t -> t.getVibrators().get(1).isVibrating() - && t.getVibrators().get(2).isVibrating() - && t.getVibrators().get(3).isVibrating(), + t -> mControllers.get(1).isVibrating() + && mControllers.get(2).isVibrating() + && mControllers.get(3).isVibrating(), thread, TEST_TIMEOUT_MILLIS)); waitForCompletion(thread); @@ -954,9 +957,9 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); - assertFalse(thread.getVibrators().get(1).isVibrating()); - assertFalse(thread.getVibrators().get(2).isVibrating()); - assertFalse(thread.getVibrators().get(3).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); + assertFalse(mControllers.get(3).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(25)), mVibratorProviders.get(1).getEffectSegments()); @@ -1031,7 +1034,7 @@ public class VibrationThreadTest { // After the vibrator call ends the vibration is cancelled and the vibrator is turned off. waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @Test @@ -1051,7 +1054,7 @@ public class VibrationThreadTest { .combine(); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread, + assertTrue(waitUntil(t -> mControllers.get(2).isVibrating(), vibrationThread, TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); @@ -1064,8 +1067,8 @@ public class VibrationThreadTest { waitForCompletion(cancellingThread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(vibrationThread.getVibrators().get(1).isVibrating()); - assertFalse(vibrationThread.getVibrators().get(2).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); } @Test @@ -1082,8 +1085,8 @@ public class VibrationThreadTest { .combine(); VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(1).isVibrating() - && t.getVibrators().get(2).isVibrating(), + assertTrue(waitUntil(t -> mControllers.get(1).isVibrating() + && mControllers.get(2).isVibrating(), vibrationThread, TEST_TIMEOUT_MILLIS)); assertTrue(vibrationThread.isAlive()); @@ -1096,8 +1099,8 @@ public class VibrationThreadTest { waitForCompletion(cancellingThread); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); - assertFalse(vibrationThread.getVibrators().get(1).isVibrating()); - assertFalse(vibrationThread.getVibrators().get(2).isVibrating()); + assertFalse(mControllers.get(1).isVibrating()); + assertFalse(mControllers.get(2).isVibrating()); } @Test @@ -1106,7 +1109,7 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0); VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread, + assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread, TEST_TIMEOUT_MILLIS)); assertTrue(thread.isAlive()); @@ -1117,7 +1120,7 @@ public class VibrationThreadTest { verify(mVibrationToken).unlinkToDeath(same(thread), eq(0)); verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); - assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); + assertFalse(mControllers.get(VIBRATOR_ID).isVibrating()); } @Test @@ -1188,7 +1191,7 @@ public class VibrationThreadTest { long vibrationId = 1; VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240); VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); - assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread, + assertTrue(waitUntil(t -> mControllers.get(VIBRATOR_ID).isVibrating(), thread, TEST_TIMEOUT_MILLIS)); thread.cancel(); waitForCompletion(thread); @@ -1295,8 +1298,9 @@ public class VibrationThreadTest { } private VibrationThread startThreadAndDispatcher(Vibration vib) { + mControllers = createVibratorControllers(); VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter, - createVibratorControllers(), mWakeLock, mManagerHooks); + mControllers, mWakeLock, mManagerHooks); doAnswer(answer -> { thread.vibratorComplete(answer.getArgument(0)); return null; diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 8d60466eff95..4cddd8506df7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -60,7 +60,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index 7ee6451b2797..5bd365c7eefd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -64,7 +64,6 @@ open class ImeAppHelper @JvmOverloads constructor( device.waitForIdle() } else { wmHelper.waitImeShown() - wmHelper.waitForAppTransitionIdle() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index b66c45c7c9f0..a135e0af067b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -53,8 +53,8 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( button.click() device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT) - wmHelper.waitForFullScreenApp(secondActivityComponent) wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(secondActivityComponent), WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), WindowManagerConditionsFactory.hasLayersAnimating().negate() ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index ba5698cafa15..a9564fdd2332 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -88,7 +88,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { } transitions { device.reopenAppFromOverview(wmHelper) - require(wmHelper.waitImeShown()) { "IME didn't show in time" } + wmHelper.waitImeShown() } teardown { test { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt index 19e2c92304d4..7e3ed8252f4c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.view.Display import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -35,6 +36,8 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarWindowIsVisible import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.common.WindowManagerConditionsFactory +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test @@ -64,12 +67,22 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame eachRun { this.setRotation(testSpec.startRotation) testApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - wmHelper.waitForAppTransitionIdle() + val testAppVisible = wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(testApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle( + Display.DEFAULT_DISPLAY)) + require(testAppVisible) { + "Expected ${testApp.component.toWindowName()} to be visible" + } imeTestApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - wmHelper.waitForAppTransitionIdle() + val imeAppVisible = wmHelper.waitFor( + WindowManagerStateHelper.isAppFullScreen(imeTestApp.component), + WindowManagerConditionsFactory.isAppTransitionIdle( + Display.DEFAULT_DISPLAY)) + require(imeAppVisible) { + "Expected ${imeTestApp.component.toWindowName()} to be visible" + } imeTestApp.openIME(device, wmHelper) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index b5e13be7dca0..cc808a0ce871 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.view.Display import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.entireScreenCovered @@ -30,7 +31,9 @@ import com.android.server.wm.flicker.annotation.Group4 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.common.WindowManagerConditionsFactory import com.android.server.wm.traces.parser.toFlickerComponent +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -77,14 +80,16 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) { } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } transitions { testApp.openSecondActivity(device, wmHelper) device.pressBack() - wmHelper.waitForAppTransitionIdle() - wmHelper.waitForFullScreenApp(testApp.component) + val firstActivityVisible = wmHelper.waitFor( + WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY), + WindowManagerStateHelper.isAppFullScreen(testApp.component)) + require(firstActivityVisible) { "Expected ${testApp.component} to be visible" } } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 53560cc14c44..4313b8dbc883 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -56,7 +56,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { } teardown { test { - testApp.exit() + testApp.exit(wmHelper) } } } |