diff options
73 files changed, 1404 insertions, 250 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl index 9d11ca470397..25caf4b695bb 100644 --- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl @@ -40,7 +40,6 @@ interface IAlarmManager { long getNextWakeFromIdleTime(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); - long currentNetworkTimeMillis(); boolean canScheduleExactAlarms(String packageName); boolean hasScheduleExactAlarm(String packageName, int userId); int getConfigVersion(); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 296c89c66e4c..c053b2ed5adb 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -90,7 +90,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.ParcelableException; import android.os.PowerExemptionManager; import android.os.PowerManager; import android.os.Process; @@ -116,7 +115,6 @@ import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongArrayQueue; -import android.util.NtpTrustedTime; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -161,7 +159,6 @@ import libcore.util.EmptyArray; import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.SimpleDateFormat; -import java.time.DateTimeException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -3008,17 +3005,6 @@ public class AlarmManagerService extends SystemService { } @Override - public long currentNetworkTimeMillis() { - final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext()); - NtpTrustedTime.TimeResult ntpResult = time.getCachedTimeResult(); - if (ntpResult != null) { - return ntpResult.currentTimeMillis(); - } else { - throw new ParcelableException(new DateTimeException("Missing NTP fix")); - } - } - - @Override public int getConfigVersion() { getContext().enforceCallingOrSelfPermission(Manifest.permission.DUMP, "getConfigVersion"); diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index a3a361501b1c..f29998af7ae1 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -290,10 +290,10 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void editProperties(IAccountAuthenticatorResponse response, String accountType) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.editProperties( new AccountAuthenticatorResponse(response), accountType); @@ -305,10 +305,10 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void hasFeatures(IAccountAuthenticatorResponse response, Account account, String[] features) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.hasFeatures( new AccountAuthenticatorResponse(response), account, features); @@ -320,10 +320,10 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void getAccountRemovalAllowed(IAccountAuthenticatorResponse response, Account account) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.getAccountRemovalAllowed( new AccountAuthenticatorResponse(response), account); @@ -335,10 +335,10 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void getAccountCredentialsForCloning(IAccountAuthenticatorResponse response, Account account) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.getAccountCredentialsForCloning( @@ -351,11 +351,11 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void addAccountFromCredentials(IAccountAuthenticatorResponse response, Account account, Bundle accountCredentials) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.addAccountFromCredentials( @@ -465,12 +465,12 @@ public abstract class AbstractAccountAuthenticator { } } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCOUNT_MANAGER) @Override public void isCredentialsUpdateSuggested( IAccountAuthenticatorResponse response, Account account, String statusToken) throws RemoteException { - checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this .isCredentialsUpdateSuggested( diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl index 701cecf38f32..0575c07592b6 100644 --- a/core/java/android/accounts/IAccountAuthenticator.aidl +++ b/core/java/android/accounts/IAccountAuthenticator.aidl @@ -62,6 +62,7 @@ oneway interface IAccountAuthenticator { /** * launches an activity that lets the user edit and set the properties for an authenticator */ + @EnforcePermission("ACCOUNT_MANAGER") @UnsupportedAppUsage void editProperties(in IAccountAuthenticatorResponse response, String accountType); @@ -69,6 +70,7 @@ oneway interface IAccountAuthenticator { * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the * specified features */ + @EnforcePermission("ACCOUNT_MANAGER") @UnsupportedAppUsage void hasFeatures(in IAccountAuthenticatorResponse response, in Account account, in String[] features); @@ -76,12 +78,14 @@ oneway interface IAccountAuthenticator { /** * Gets whether or not the account is allowed to be removed. */ + @EnforcePermission("ACCOUNT_MANAGER") @UnsupportedAppUsage void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account); /** * Returns a Bundle containing the required credentials to copy the account across users. */ + @EnforcePermission("ACCOUNT_MANAGER") void getAccountCredentialsForCloning(in IAccountAuthenticatorResponse response, in Account account); @@ -89,6 +93,7 @@ oneway interface IAccountAuthenticator { * Uses the Bundle containing credentials from another instance of the authenticator to create * a copy of the account on this user. */ + @EnforcePermission("ACCOUNT_MANAGER") void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account, in Bundle accountCredentials); @@ -116,6 +121,7 @@ oneway interface IAccountAuthenticator { /** * Checks if the credentials of the provided account should be updated. */ + @EnforcePermission("ACCOUNT_MANAGER") void isCredentialsUpdateSuggested(in IAccountAuthenticatorResponse response, in Account account, String statusToken); } diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index fc7afb483142..b441359b1614 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -24,6 +24,7 @@ import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; +import android.app.timedetector.TimePoint; /** * System private API to communicate with time detector service. @@ -45,9 +46,11 @@ interface ITimeDetectorService { boolean updateConfiguration(in TimeConfiguration timeConfiguration); - void suggestExternalTime( in ExternalTimeSuggestion timeSuggestion); + void suggestExternalTime(in ExternalTimeSuggestion timeSuggestion); void suggestGnssTime(in GnssTimeSuggestion timeSuggestion); boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion); void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion); void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion); + + TimePoint latestNetworkTime(); } diff --git a/core/java/android/app/timedetector/TimePoint.aidl b/core/java/android/app/timedetector/TimePoint.aidl new file mode 100644 index 000000000000..80d4bc1c34b5 --- /dev/null +++ b/core/java/android/app/timedetector/TimePoint.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +parcelable TimePoint; diff --git a/core/java/android/app/timedetector/TimePoint.java b/core/java/android/app/timedetector/TimePoint.java new file mode 100644 index 000000000000..aa079a9b1159 --- /dev/null +++ b/core/java/android/app/timedetector/TimePoint.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timedetector; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Data class for passing a Unix epoch time anchored to the elapsed realtime clock. + * + * @hide + */ +public final class TimePoint implements Parcelable { + + private final long mUnixEpochTimeMillis; + private final long mElapsedRealtimeMillis; + + public TimePoint(long unixEpochTimeMillis, long elapsedRealtimeMillis) { + mUnixEpochTimeMillis = unixEpochTimeMillis; + mElapsedRealtimeMillis = elapsedRealtimeMillis; + } + + /** + * The current Unix epoch time, according to the external source. + */ + public long getUnixEpochTimeMillis() { + return mUnixEpochTimeMillis; + } + + /** + * The elapsed millis since boot when {@link #getUnixEpochTimeMillis} was computed. + */ + public long getElapsedRealtimeMillis() { + return mElapsedRealtimeMillis; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mUnixEpochTimeMillis); + out.writeLong(mElapsedRealtimeMillis); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TimePoint)) { + return false; + } + TimePoint timePoint = (TimePoint) o; + return mUnixEpochTimeMillis == timePoint.mUnixEpochTimeMillis + && mElapsedRealtimeMillis == timePoint.mElapsedRealtimeMillis; + } + + @Override + public int hashCode() { + return Objects.hash(mUnixEpochTimeMillis, mElapsedRealtimeMillis); + } + + @Override + public String toString() { + return "TimePoint{" + + "mUnixEpochTimeMillis=" + mUnixEpochTimeMillis + + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis + + '}'; + } + + public static final @NonNull Creator<TimePoint> CREATOR = + new Creator<TimePoint>() { + public TimePoint createFromParcel(Parcel in) { + long unixEpochTime = in.readLong(); + long elapsedRealtimeMillis = in.readLong(); + return new TimePoint(unixEpochTime, elapsedRealtimeMillis); + } + + public TimePoint[] newArray(int size) { + return new TimePoint[size]; + } + }; +} diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 4d0ba63d7759..336ef7ac78db 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -1739,6 +1739,20 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { // abruptly. Log.w(TAG, "Output surface likely abandoned, dropping buffer!"); img.close(); + } catch (RuntimeException e) { + // NOTE: This is intended to catch RuntimeException from ImageReader.detachImage + // ImageReader.detachImage is not supposed to throw RuntimeExceptions but the + // bug went unchecked for a few years and now its behavior cannot be changed + // without breaking backwards compatibility. + + if (!e.getClass().equals(RuntimeException.class)) { + // re-throw any exceptions that aren't base RuntimeException since they are + // coming from elsewhere, and we shouldn't silently drop those. + throw e; + } + + Log.w(TAG, "Output surface likely abandoned, dropping buffer!"); + img.close(); } } } @@ -1773,9 +1787,23 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } try { reader.detachImage(img); - } catch (Exception e) { - Log.e(TAG, - "Failed to detach image!"); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to detach image!"); + img.close(); + return; + } catch (RuntimeException e) { + // NOTE: This is intended to catch RuntimeException from ImageReader.detachImage + // ImageReader.detachImage is not supposed to throw RuntimeExceptions but the + // bug went unchecked for a few years and now its behavior cannot be changed + // without breaking backwards compatibility. + + if (!e.getClass().equals(RuntimeException.class)) { + // re-throw any exceptions that aren't base RuntimeException since they are + // coming from elsewhere, and we shouldn't silently drop those. + throw e; + } + + Log.e(TAG, "Failed to detach image!"); img.close(); return; } diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java index 8acd1ff27917..20d6338ec927 100644 --- a/core/java/android/hardware/location/ActivityRecognitionHardware.java +++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java @@ -88,34 +88,34 @@ public class ActivityRecognitionHardware extends IActivityRecognitionHardware.St return nativeIsSupported(); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public String[] getSupportedActivities() { - checkPermissions(); return mSupportedActivities; } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean isActivitySupported(String activity) { - checkPermissions(); int activityType = getActivityType(activity); return activityType != INVALID_ACTIVITY_TYPE; } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean registerSink(IActivityRecognitionHardwareSink sink) { - checkPermissions(); return mSinks.register(sink); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean unregisterSink(IActivityRecognitionHardwareSink sink) { - checkPermissions(); return mSinks.unregister(sink); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) { - checkPermissions(); int activityType = getActivityType(activity); if (activityType == INVALID_ACTIVITY_TYPE) { @@ -130,9 +130,9 @@ public class ActivityRecognitionHardware extends IActivityRecognitionHardware.St return false; } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean disableActivityEvent(String activity, int eventType) { - checkPermissions(); int activityType = getActivityType(activity); if (activityType == INVALID_ACTIVITY_TYPE) { @@ -147,9 +147,9 @@ public class ActivityRecognitionHardware extends IActivityRecognitionHardware.St return false; } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean flush() { - checkPermissions(); int result = nativeFlush(); return result == NATIVE_SUCCESS_RESULT; } diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java index c0bcb273f9b7..106bfd56a7af 100644 --- a/core/java/android/hardware/location/GeofenceHardwareService.java +++ b/core/java/android/hardware/location/GeofenceHardwareService.java @@ -75,76 +75,68 @@ public class GeofenceHardwareService extends Service { mGeofenceHardwareImpl.setFusedGeofenceHardware(service); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public int[] getMonitoringTypes() { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); return mGeofenceHardwareImpl.getMonitoringTypes(); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public int getStatusOfMonitoringType(int monitoringType) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); return mGeofenceHardwareImpl.getStatusOfMonitoringType(monitoringType); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean addCircularFence( int monitoringType, GeofenceHardwareRequestParcelable request, IGeofenceHardwareCallback callback) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.addCircularFence(monitoringType, request, callback); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean removeGeofence(int id, int monitoringType) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.removeGeofence(id, monitoringType); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean pauseGeofence(int id, int monitoringType) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean resumeGeofence(int id, int monitoringType, int monitorTransitions) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.resumeGeofence(id, monitoringType, monitorTransitions); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean registerForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.registerForMonitorStateChangeCallback(monitoringType, callback); } + @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) @Override public boolean unregisterForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) { - mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to access hardware geofence"); checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType); return mGeofenceHardwareImpl.unregisterForMonitorStateChangeCallback(monitoringType, diff --git a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl index bc6b1830cd4f..f093cd565ce3 100644 --- a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl +++ b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl @@ -28,35 +28,42 @@ interface IActivityRecognitionHardware { /** * Gets an array of supported activities by hardware. */ + @EnforcePermission("LOCATION_HARDWARE") String[] getSupportedActivities(); /** * Returns true if the given activity is supported, false otherwise. */ + @EnforcePermission("LOCATION_HARDWARE") boolean isActivitySupported(in String activityType); /** * Registers a sink with Hardware Activity-Recognition. */ + @EnforcePermission("LOCATION_HARDWARE") boolean registerSink(in IActivityRecognitionHardwareSink sink); /** * Unregisters a sink with Hardware Activity-Recognition. */ + @EnforcePermission("LOCATION_HARDWARE") boolean unregisterSink(in IActivityRecognitionHardwareSink sink); /** * Enables tracking of a given activity/event type, if the activity is supported. */ + @EnforcePermission("LOCATION_HARDWARE") boolean enableActivityEvent(in String activityType, int eventType, long reportLatencyNs); /** * Disables tracking of a given activity/eventy type. */ + @EnforcePermission("LOCATION_HARDWARE") boolean disableActivityEvent(in String activityType, int eventType); /** * Requests hardware for all the activity events detected up to the given point in time. */ + @EnforcePermission("LOCATION_HARDWARE") boolean flush(); }
\ No newline at end of file diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl index 0e840c4906fd..41804addbcba 100644 --- a/core/java/android/hardware/location/IGeofenceHardware.aidl +++ b/core/java/android/hardware/location/IGeofenceHardware.aidl @@ -26,17 +26,25 @@ import android.hardware.location.IGeofenceHardwareMonitorCallback; interface IGeofenceHardware { void setGpsGeofenceHardware(in IGpsGeofenceHardware service); void setFusedGeofenceHardware(in IFusedGeofenceHardware service); + @EnforcePermission("LOCATION_HARDWARE") int[] getMonitoringTypes(); + @EnforcePermission("LOCATION_HARDWARE") int getStatusOfMonitoringType(int monitoringType); + @EnforcePermission("LOCATION_HARDWARE") boolean addCircularFence( int monitoringType, in GeofenceHardwareRequestParcelable request, in IGeofenceHardwareCallback callback); + @EnforcePermission("LOCATION_HARDWARE") boolean removeGeofence(int id, int monitoringType); + @EnforcePermission("LOCATION_HARDWARE") boolean pauseGeofence(int id, int monitoringType); + @EnforcePermission("LOCATION_HARDWARE") boolean resumeGeofence(int id, int monitoringType, int monitorTransitions); + @EnforcePermission("LOCATION_HARDWARE") boolean registerForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback); + @EnforcePermission("LOCATION_HARDWARE") boolean unregisterForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index c02f870edbc2..c6fc9ec0ee6b 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2163,13 +2163,13 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesVisibility = vis; } } - + /** * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE} * or {@link View#GONE View.GONE}) of the candidates view when it is not * shown. The default implementation returns GONE when * {@link #isExtractViewShown} returns true, - * otherwise VISIBLE. Be careful if you change this to return GONE in + * otherwise INVISIBLE. Be careful if you change this to return GONE in * other situations -- if showing or hiding the candidates view causes * your window to resize, this can cause temporary drawing artifacts as * the resize takes place. diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index 7379443877b7..ecea054db216 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -18,6 +18,8 @@ package android.os; import android.annotation.NonNull; import android.app.IAlarmManager; +import android.app.timedetector.ITimeDetectorService; +import android.app.timedetector.TimePoint; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.location.ILocationManager; @@ -170,6 +172,14 @@ public final class SystemClock { return false; } + private static IAlarmManager getIAlarmManager() { + if (sIAlarmManager == null) { + sIAlarmManager = IAlarmManager.Stub + .asInterface(ServiceManager.getService(Context.ALARM_SERVICE)); + } + return sIAlarmManager; + } + /** * Returns milliseconds since boot, not counting time spent in deep sleep. * @@ -269,56 +279,71 @@ public final class SystemClock { * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted - * by the user. Note that synchronization may occur using an insecure - * network protocol, so the returned time should not be used for security - * purposes. + * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. + * <p> + * Note that synchronization may occur using an insecure network protocol, + * so the returned time should not be used for security purposes. + * The device may resynchronize with the same or different network source + * at any time. Due to network delays, variations between servers, or local + * (client side) clock drift, the accuracy of the returned times cannot be + * guaranteed. In extreme cases, consecutive calls to {@link + * #currentNetworkTimeMillis()} could return times that are out of order. * - * @throws DateTimeException when no accurate network time can be provided. + * @throws DateTimeException when no network time can be provided. * @hide */ public static long currentNetworkTimeMillis() { - final IAlarmManager mgr = getIAlarmManager(); - if (mgr != null) { + ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub + .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)); + if (timeDetectorService != null) { + TimePoint time; try { - return mgr.currentNetworkTimeMillis(); + time = timeDetectorService.latestNetworkTime(); } catch (ParcelableException e) { e.maybeRethrow(DateTimeException.class); throw new RuntimeException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } + + if (time == null) { + // This is not expected. + throw new DateTimeException("Network based time is not available."); + } + long currentMillis = elapsedRealtime(); + long deltaMs = currentMillis - time.getElapsedRealtimeMillis(); + return time.getUnixEpochTimeMillis() + deltaMs; } else { throw new RuntimeException(new DeadSystemException()); } } - private static IAlarmManager getIAlarmManager() { - if (sIAlarmManager == null) { - sIAlarmManager = IAlarmManager.Stub - .asInterface(ServiceManager.getService(Context.ALARM_SERVICE)); - } - return sIAlarmManager; - } - - /** + /** * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC, * synchronized using a remote network source outside the device. * <p> * While the time returned by {@link System#currentTimeMillis()} can be * adjusted by the user, the time returned by this method cannot be adjusted - * by the user. Note that synchronization may occur using an insecure - * network protocol, so the returned time should not be used for security - * purposes. + * by the user. * <p> * This performs no blocking network operations and returns values based on * a recent successful synchronization event; it will either return a valid * time or throw. + * <p> + * Note that synchronization may occur using an insecure network protocol, + * so the returned time should not be used for security purposes. + * The device may resynchronize with the same or different network source + * at any time. Due to network delays, variations between servers, or local + * (client side) clock drift, the accuracy of the returned times cannot be + * guaranteed. In extreme cases, consecutive calls to {@link + * Clock#millis()} on the returned {@link Clock}could return times that are + * out of order. * - * @throws DateTimeException when no accurate network time can be provided. + * @throws DateTimeException when no network time can be provided. */ public static @NonNull Clock currentNetworkTimeClock() { return new SimpleClock(ZoneOffset.UTC) { diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 4e7b3a51d758..4a3f772d3bc6 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -55,18 +55,19 @@ public class NtpTrustedTime implements TrustedTime { * @hide */ public static class TimeResult { - private final long mTimeMillis; + private final long mUnixEpochTimeMillis; private final long mElapsedRealtimeMillis; private final long mCertaintyMillis; - public TimeResult(long timeMillis, long elapsedRealtimeMillis, long certaintyMillis) { - mTimeMillis = timeMillis; + public TimeResult( + long unixEpochTimeMillis, long elapsedRealtimeMillis, long certaintyMillis) { + mUnixEpochTimeMillis = unixEpochTimeMillis; mElapsedRealtimeMillis = elapsedRealtimeMillis; mCertaintyMillis = certaintyMillis; } public long getTimeMillis() { - return mTimeMillis; + return mUnixEpochTimeMillis; } public long getElapsedRealtimeMillis() { @@ -77,9 +78,11 @@ public class NtpTrustedTime implements TrustedTime { return mCertaintyMillis; } - /** Calculates and returns the current time accounting for the age of this result. */ + /** + * Calculates and returns the current Unix epoch time accounting for the age of this result. + */ public long currentTimeMillis() { - return mTimeMillis + getAgeMillis(); + return mUnixEpochTimeMillis + getAgeMillis(); } /** Calculates and returns the age of this result. */ @@ -99,7 +102,7 @@ public class NtpTrustedTime implements TrustedTime { @Override public String toString() { return "TimeResult{" - + "mTimeMillis=" + Instant.ofEpochMilli(mTimeMillis) + + "mUnixEpochTimeMillis=" + Instant.ofEpochMilli(mUnixEpochTimeMillis) + ", mElapsedRealtimeMillis=" + Duration.ofMillis(mElapsedRealtimeMillis) + ", mCertaintyMillis=" + mCertaintyMillis + '}'; diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 872e65a3ac55..07dfce780c6c 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -61,22 +61,32 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; /** - * A Switch is a two-state toggle switch widget that can select between two - * options. The user may drag the "thumb" back and forth to choose the selected option, - * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text} - * property controls the text displayed in the label for the switch, whereas the - * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text - * controls the text on the thumb. Similarly, the - * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related - * setTypeface() methods control the typeface and style of label text, whereas the - * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and - * the related setSwitchTypeface() methods control that of the thumb. + * A Switch is a two-state toggle widget. Users can drag the switch "thumb" back + * and forth to select either of two options or simply tap the switch to toggle + * between options. * - * <p>{@link androidx.recyclerview.widget.RecyclerView} is a version of - * the Switch widget which runs on devices back to API 7.</p> + * <p>The {@link #setText(CharSequence) text} property controls + * the text of the switch label. The {@link #setTextOn(CharSequence) textOn} and + * {@link #setTextOff(CharSequence) textOff} properties control the text of the + * thumb. The {@link #setTextAppearance(int) textAppearance} property and the + * related {@link #setTypeface(android.graphics.Typeface) setTypeface()} methods + * control the typeface and style of the switch label. The + * {@link #setSwitchTextAppearance(android.content.Context, int) + * switchTextAppearance} property and the related + * {@link #setSwitchTypeface(android.graphics.Typeface) setSwitchTypeface()} + * methods control the typeface and style of the thumb text.</p> * - * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a> - * guide.</p> + * <p class="note"><b>Note:</b> The thumb text is displayed only if the + * <a href="{@docRoot}reference/android/widget/Switch#attr_android:showText"> + * <code>showText</code></a> attribute is set to {@code true}. See also + * {@link #setShowText(boolean)} and {@link #getShowText()}.</p> + * + * <p>{@link androidx.appcompat.widget.SwitchCompat} provides backward + * compatibility down to Android 4.0 (API level 14).</p> + * + * <p>For more information, see the + * <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html"> + * Toggle Buttons</a> guide.</p> * * @attr ref android.R.styleable#Switch_textOn * @attr ref android.R.styleable#Switch_textOff diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index acadac78ce13..bb4ab39a59d1 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -549,6 +549,11 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na return NULL; } + if (parcel->isForRpc()) { + jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall an RPC Parcel."); + return NULL; + } + if (parcel->objectsCount()) { jniThrowException(env, "java/lang/RuntimeException", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java index 14ba9df93f24..764e650a807c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java @@ -85,6 +85,8 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer { } mDisplayAreasInfo.remove(displayId); + mLeashes.get(displayId).release(); + mLeashes.remove(displayId); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index fb618617afaf..9df82514a4e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -530,7 +530,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } final int taskId = taskInfo.taskId; - final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo()); + final TaskAppearedInfo appearedInfo = mTasks.get(taskId); + final TaskListener listener = getTaskListener(appearedInfo.getTaskInfo()); mTasks.remove(taskId); if (listener != null) { listener.onTaskVanished(taskInfo); @@ -540,6 +541,10 @@ public class ShellTaskOrganizer extends TaskOrganizer implements notifyCompatUI(taskInfo, null /* taskListener */); // Notify the recent tasks that a task has been removed mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); + + if (appearedInfo.getLeash() != null) { + appearedInfo.getLeash().release(); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index f061f8bc178a..e35e4ac8881c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -406,7 +406,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean shouldDispatchToLauncher(int backType) { return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && mBackToLauncherCallback != null - && mEnableAnimations.get(); + && mEnableAnimations.get() + && mBackNavigationInfo != null + && mBackNavigationInfo.getDepartingAnimationTarget() != null; } private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 3f7d78dda037..9478b347653f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -128,9 +128,10 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { final WindowContainerTransaction wct = new WindowContainerTransaction(); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - applyBoundsAndOffsets( - displayAreaInfo.token, mDisplayAreaMap.get(displayAreaInfo.token), wct, t); + final SurfaceControl leash = mDisplayAreaMap.get(displayAreaInfo.token); + applyBoundsAndOffsets(displayAreaInfo.token, leash, wct, t); applyTransaction(wct, t); + leash.release(); mDisplayAreaMap.remove(displayAreaInfo.token); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index f61d1b95bd85..451afa08040c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -159,6 +159,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) { + final SurfaceControl leash = mDisplayAreaTokenMap.get(displayAreaInfo.token); + if (leash != null) { + leash.release(); + } mDisplayAreaTokenMap.remove(displayAreaInfo.token); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 22b0ccbc8488..da88c2de6c01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -943,6 +943,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mMainExecutor.executeDelayed(() -> mPipMenuController.detach(), 0); + mLeash = null; if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 046b1a2541b4..c8ee010e60e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -486,7 +486,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) { - return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/); + try { + return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/); + } finally { + for (RemoteAnimationTarget appTarget : apps) { + if (appTarget.leash != null) { + appTarget.leash.release(); + } + } + } } private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps, 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 55e7dacc4087..70d728d90b6a 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 @@ -1024,6 +1024,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mRootTaskInfo = null; + mRootTaskLeash = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 99e750629729..cb9e1a39ec52 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -243,6 +243,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { if (mRootTaskInfo.taskId == taskId) { mCallbacks.onRootTaskVanished(); mRootTaskInfo = null; + mRootLeash = null; mSyncQueue.runInSync(t -> { t.remove(mDimLayer); mSplitDecorManager.release(t); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 2b6c0da99eea..ce624f2b5bbe 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.FlakyTest import androidx.test.filters.RequiresDevice import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -51,7 +51,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Postsubmit +@FlakyTest(bugId = 238367575) @Group3 class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) { protected val taplInstrumentation = LauncherInstrumentation() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 0b53c4069c3f..494815850b7c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -46,6 +46,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.SparseArray; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; @@ -137,13 +138,25 @@ public class ShellTaskOrganizerTests { } @Test - public void registerOrganizer_sendRegisterTaskOrganizer() throws RemoteException { + public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException { mOrganizer.registerOrganizer(); verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class)); } @Test + public void testTaskLeashReleasedAfterVanished() throws RemoteException { + RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + SurfaceControl taskLeash = new SurfaceControl.Builder(new SurfaceSession()) + .setName("task").build(); + mOrganizer.registerOrganizer(); + mOrganizer.onTaskAppeared(taskInfo, taskLeash); + assertTrue(taskLeash.isValid()); + mOrganizer.onTaskVanished(taskInfo); + assertTrue(!taskLeash.isValid()); + } + + @Test public void testOneListenerPerType() { mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); try { diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index ded9597b68ef..546f0c665a98 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -1286,6 +1286,8 @@ public final class AudioAttributes implements Parcelable { /** * Specifying if haptic should be muted or not when playing audio-haptic coupled data. * By default, haptic channels are disabled. + * <p>This will be ignored if the caller doesn't have the + * {@link android.Manifest.permission#VIBRATE} permission. * @param muted true to force muting haptic channels. * @return the same Builder instance. */ diff --git a/media/java/android/media/audiofx/HapticGenerator.java b/media/java/android/media/audiofx/HapticGenerator.java index fe7f29e1fc00..d2523ef43b9e 100644 --- a/media/java/android/media/audiofx/HapticGenerator.java +++ b/media/java/android/media/audiofx/HapticGenerator.java @@ -91,7 +91,8 @@ public class HapticGenerator extends AudioEffect implements AutoCloseable { } /** - * Enable or disable the effect. + * Enable or disable the effect. The effect can only be enabled if the caller has the + * {@link android.Manifest.permission#VIBRATE} permission. * * @param enabled the requested enable state * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index db416013c453..a66dc7743792 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -427,18 +427,14 @@ public class RemoteTransitionCompat implements Parcelable { mPipTransaction = null; } } - // Release surface references now. This is apparently to free GPU - // memory while doing quick operations (eg. during CTS). - for (int i = 0; i < mLeashMap.size(); ++i) { - if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue; - t.remove(mLeashMap.valueAt(i)); - } try { mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); } catch (RemoteException e) { Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e); t.apply(); } + // Only release the non-local created surface references. The animator is responsible + // for releasing the leashes created by local. for (int i = 0; i < mInfo.getChanges().size(); ++i) { mInfo.getChanges().get(i).getLeash().release(); } diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt index 4986fe85af19..99267e8ee1c9 100644 --- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt @@ -125,6 +125,13 @@ class CameraGestureHelper @Inject constructor( // launched from behind the lock-screen. activityStarter.startActivity(intent, false /* dismissShade */) } + + // Call this to make sure that the keyguard returns if the app that is being launched + // crashes after a timeout. + centralSurfaces.startLaunchTransitionTimeout() + // Call this to make sure the keyguard is ready to be dismissed once the next intent is + // handled by the OS (in our case it is the activity we started right above) + centralSurfaces.readyForKeyguardDone() } /** diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 2fa104a7ce18..e73eaa1da4ae 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -176,7 +176,7 @@ public class Flags { // 1000 - dock public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING = new BooleanFlag(1000, true); - public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, false); + public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true); // 1100 - windowing diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 2f732de50ea1..458ed4059b3b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -144,8 +144,7 @@ class MediaHierarchyManager @Inject constructor( animatedFraction) // When crossfading, let's keep the bounds at the right location during fading boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f - currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress, - instantlyShowAtEnd = false) + currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress) } else { // If we're not crossfading, let's interpolate from the start alpha to 1.0f currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction) @@ -276,7 +275,7 @@ class MediaHierarchyManager @Inject constructor( if (value >= 0) { updateTargetState() // Setting the alpha directly, as the below call will use it to update the alpha - carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true) + carouselAlpha = calculateAlphaFromCrossFade(field) applyTargetStateIfNotAnimating() } } @@ -414,18 +413,10 @@ class MediaHierarchyManager @Inject constructor( * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching * between the start and the end location and the content is fully faded, while 0.75f means * that we're halfway faded in again in the target state. - * - * @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed - * to avoid fadinging in when the target was hidden anyway. */ - private fun calculateAlphaFromCrossFade( - crossFadeProgress: Float, - instantlyShowAtEnd: Boolean - ): Float { + private fun calculateAlphaFromCrossFade(crossFadeProgress: Float): Float { if (crossFadeProgress <= 0.5f) { return 1.0f - crossFadeProgress / 0.5f - } else if (instantlyShowAtEnd) { - return 1.0f } else { return (crossFadeProgress - 0.5f) / 0.5f } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 5f52485a5481..281ef9495d8e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -626,6 +626,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements } }, mainExecutor, bgExecutor); + mView.setBackgroundExecutor(bgExecutor); mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler); mNavBarMode = mNavigationModeController.addListener(mModeChangedListener); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index ad3cfa359a52..b01dca1a0365 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -88,6 +88,7 @@ import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** */ @@ -97,6 +98,8 @@ public class NavigationBarView extends FrameLayout { final static boolean ALTERNATE_CAR_MODE_UI = false; + private Executor mBgExecutor; + // The current view is one of mHorizontal or mVertical depending on the current configuration View mCurrentView = null; private View mVertical; @@ -349,6 +352,10 @@ public class NavigationBarView extends FrameLayout { notifyVerticalChangedListener(mIsVertical); } + public void setBackgroundExecutor(Executor bgExecutor) { + mBgExecutor = bgExecutor; + } + public void setTouchHandler(Gefingerpoken touchHandler) { mTouchHandler = touchHandler; } @@ -768,8 +775,8 @@ public class NavigationBarView extends FrameLayout { updateSlippery(); reloadNavIcons(); updateNavButtonIcons(); - WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled( - !mShowSwipeUpUi); + mBgExecutor.execute(() -> WindowManagerWrapper.getInstance() + .setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi)); getHomeButton().setAccessibilityDelegate( mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 8179d1763ea1..a83367059736 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -321,7 +321,10 @@ class BackPanelController private constructor( velocityTracker = null } MotionEvent.ACTION_CANCEL -> { - updateArrowState(GestureState.CANCELLED) + // Receiving a CANCEL implies that something else intercepted + // the gesture, i.e., the user did not cancel their gesture. + // Therefore, disappear immediately, with minimum fanfare. + updateArrowState(GestureState.GONE) velocityTracker = null } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index fcafeada9d9a..0697133a02f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -15,6 +15,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; @@ -626,6 +627,16 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } } + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mAdapter != null && mAdapter.getCount() > 0) { + event.setItemCount(mAdapter.getCount()); + event.setFromIndex(getCurrentPageNumber()); + event.setToIndex(getCurrentPageNumber()); + } + } + private static Animator setupBounceAnimator(View view, int ordinal) { view.setAlpha(0f); view.setScaleX(0f); diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 09ed56745e54..1b669322e553 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -175,12 +176,15 @@ public final class PermissionHelper { mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId, TAG); } + int flagMask = userSet || !grant + ? FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT : + FLAG_PERMISSION_USER_SET; if (userSet) { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, userId); + flagMask, FLAG_PERMISSION_USER_SET, true, userId); } else { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - 0, FLAG_PERMISSION_USER_SET, true, userId); + flagMask, 0, true, userId); } } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index d68cecb41190..89bb17f37382 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -473,9 +473,13 @@ final class InstallPackageHelper { mApexManager.registerApkInApex(pkg); } - // Add the package's KeySets to the global KeySetManagerService - KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); - ksms.addScannedPackageLPw(pkg); + // Don't add keysets for APEX as their package settings are not persisted and will + // result in orphaned keysets. + if ((scanFlags & SCAN_AS_APEX) == 0) { + // Add the package's KeySets to the global KeySetManagerService + KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService(); + ksms.addScannedPackageLPw(pkg); + } final Computer snapshot = mPm.snapshotComputer(); mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage, snapshot); @@ -3464,8 +3468,9 @@ final class InstallPackageHelper { } // Sort the list to ensure we always process factory packages first Collections.sort(parseResults, (a, b) -> { - ApexInfo ai = parsingApexInfo.get(a.scanFile); - return ai.isFactory ? -1 : 1; + ApexInfo i1 = parsingApexInfo.get(a.scanFile); + ApexInfo i2 = parsingApexInfo.get(b.scanFile); + return Boolean.compare(i2.isFactory, i1.isFactory); }); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java new file mode 100644 index 000000000000..181f5adee55f --- /dev/null +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java @@ -0,0 +1,28 @@ +/* + * 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.timedetector; + +/** + * The internal (in-process) system server API for the {@link + * com.android.server.timedetector.TimeDetectorService}. + * + * <p>The methods on this class can be called from any thread. + * @hide + */ +public interface TimeDetectorInternal { + +} diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java new file mode 100644 index 000000000000..1b47ebb3caaa --- /dev/null +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java @@ -0,0 +1,42 @@ +/* + * 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.timedetector; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Handler; + +import java.util.Objects; + +/** + * The real {@link TimeDetectorInternal} local service implementation. + * + * @hide + */ +public class TimeDetectorInternalImpl implements TimeDetectorInternal { + + @NonNull private final Context mContext; + @NonNull private final Handler mHandler; + @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; + + public TimeDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, + @NonNull TimeDetectorStrategy timeDetectorStrategy) { + mContext = Objects.requireNonNull(context); + mHandler = Objects.requireNonNull(handler); + mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); + } +} diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 105cd78f2a07..02d3487ac431 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -29,15 +29,18 @@ import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; +import android.app.timedetector.TimePoint; import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.ParcelableException; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.util.ArrayMap; import android.util.IndentingPrintWriter; +import android.util.NtpTrustedTime; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -49,6 +52,7 @@ import com.android.server.timezonedetector.CallerIdentityInjector; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.time.DateTimeException; import java.util.Objects; /** @@ -79,6 +83,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub TimeDetectorStrategy timeDetectorStrategy = TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); + // Create and publish the local service for use by internal callers. + TimeDetectorInternal internal = + new TimeDetectorInternalImpl(context, handler, timeDetectorStrategy); + publishLocalService(TimeDetectorInternal.class, internal); + TimeDetectorService service = new TimeDetectorService( context, handler, serviceConfigAccessor, timeDetectorStrategy); @@ -93,6 +102,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub @NonNull private final CallerIdentityInjector mCallerIdentityInjector; @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; + @NonNull private final NtpTrustedTime mNtpTrustedTime; /** * Holds the listeners. The key is the {@link IBinder} associated with the listener, the value @@ -107,19 +117,21 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull TimeDetectorStrategy timeDetectorStrategy) { this(context, handler, serviceConfigAccessor, timeDetectorStrategy, - CallerIdentityInjector.REAL); + CallerIdentityInjector.REAL, NtpTrustedTime.getInstance(context)); } @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull TimeDetectorStrategy timeDetectorStrategy, - @NonNull CallerIdentityInjector callerIdentityInjector) { + @NonNull CallerIdentityInjector callerIdentityInjector, + @NonNull NtpTrustedTime ntpTrustedTime) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); + mNtpTrustedTime = Objects.requireNonNull(ntpTrustedTime); // Wire up a change listener so that ITimeZoneDetectorListeners can be notified when // the configuration changes for any reason. @@ -308,6 +320,19 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } @Override + public TimePoint latestNetworkTime() { + // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can + // be sure that all uses of NtpTrustedTime results in a suggestion being made to the time + // detector. mNtpTrustedTime can be removed once this happens. + NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult(); + if (ntpResult != null) { + return new TimePoint(ntpResult.getTimeMillis(), ntpResult.getElapsedRealtimeMillis()); + } else { + throw new ParcelableException(new DateTimeException("Missing network time fix")); + } + } + + @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c2b6b19f745c..fa8ea76bf0d1 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7733,9 +7733,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // relatively fixed. overrideConfig.colorMode = fullConfig.colorMode; overrideConfig.densityDpi = fullConfig.densityDpi; - // The smallest screen width is the short side of screen bounds. Because the bounds - // and density won't be changed, smallestScreenWidthDp is also fixed. - overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp; if (info.isFixedOrientation()) { // lock rotation too. When in size-compat, onConfigurationChanged will watch for and // apply runtime rotation changes. @@ -7832,7 +7829,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // computed accordingly. if (!matchParentBounds()) { getTaskFragment().computeConfigResourceOverrides(resolvedConfig, - newParentConfiguration); + newParentConfiguration, areBoundsLetterboxed()); } // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds // are already calculated in resolveFixedOrientationConfiguration. @@ -8003,7 +8000,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Since bounds has changed, the configuration needs to be computed accordingly. - getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + areBoundsLetterboxed()); } void recomputeConfiguration() { @@ -8219,7 +8217,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Calculate app bounds using fixed orientation bounds because they will be needed later // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(), - newParentConfig); + newParentConfig, mCompatDisplayInsets, areBoundsLetterboxed()); mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds); } @@ -8247,7 +8245,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Compute the configuration based on the resolved bounds. If aspect ratio doesn't // restrict, the bounds should be the requested override bounds. getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - getFixedRotationTransformDisplayInfo()); + getFixedRotationTransformDisplayInfo(), areBoundsLetterboxed()); } } @@ -8311,7 +8309,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // are calculated in compat container space. The actual position on screen will be applied // later, so the calculation is simpler that doesn't need to involve offset from parent. getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - mCompatDisplayInsets); + mCompatDisplayInsets, areBoundsLetterboxed()); // Use current screen layout as source because the size of app is independent to parent. resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride( getConfiguration().screenLayout, resolvedConfig.screenWidthDp, diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d07cc68af890..9295c18fe80e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -112,6 +112,7 @@ class BackNavigationController { RemoteAnimationTarget topAppTarget = null; int prevTaskId; int prevUserId; + boolean prepareAnimation; BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder(); synchronized (wmService.mGlobalLock) { @@ -257,7 +258,8 @@ class BackNavigationController { BackNavigationInfo.typeToString(backType)); // For now, we only animate when going home. - boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + && requestAnimation // Only create a new leash if no leash has been created. // Otherwise return null for animation target to avoid conflict. && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); @@ -292,7 +294,7 @@ class BackNavigationController { } // Special handling for back to home animation - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation && prevTask != null) { currentTask.mBackGestureStarted = true; // Make launcher show from behind by marking its top activity as visible and @@ -347,7 +349,7 @@ class BackNavigationController { Task finalTask = currentTask; RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( result, finalRemovedWindowContainer, finalBackType, finalTask, - finalprevActivity, requestAnimation)); + finalprevActivity, prepareAnimation)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } @@ -381,14 +383,14 @@ class BackNavigationController { private void onBackNavigationDone( Bundle result, WindowContainer<?> windowContainer, int backType, - Task task, ActivityRecord prevActivity, boolean requestAnimation) { + Task task, ActivityRecord prevActivity, boolean prepareAnimation) { SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); boolean triggerBack = result != null && result.getBoolean( BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " + "task=%s, prevActivity=%s", backType, task, prevActivity); - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation) { + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) { if (triggerBack) { if (surfaceControl != null && surfaceControl.isValid()) { // When going back to home, hide the task surface before it is re-parented to diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 3e6546ebb647..23adee6f6503 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1957,29 +1957,37 @@ class TaskFragment extends WindowContainer<WindowContainer> { void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - null /* compatInsets */); + null /* compatInsets */, false /* areBoundsLetterboxed */); } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) { + @NonNull Configuration parentConfig, boolean areBoundsLetterboxed) { + computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, + null /* compatInsets */, areBoundsLetterboxed); + } + + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, + boolean areBoundsLetterboxed) { if (overrideDisplayInfo != null) { // Make sure the screen related configs can be computed by the provided display info. inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; invalidateAppBoundsConfig(inOutConfig); } computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo, - null /* compatInsets */); + null /* compatInsets */, areBoundsLetterboxed); } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + @Nullable ActivityRecord.CompatDisplayInsets compatInsets, + boolean areBoundsLetterboxed) { if (compatInsets != null) { // Make sure the app bounds can be computed by the compat insets. invalidateAppBoundsConfig(inOutConfig); } computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - compatInsets); + compatInsets, areBoundsLetterboxed); } /** @@ -2006,7 +2014,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { **/ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + @Nullable ActivityRecord.CompatDisplayInsets compatInsets, + boolean areBoundsLetterboxed) { int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = parentConfig.windowConfiguration.getWindowingMode(); @@ -2113,6 +2122,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { : overrideScreenHeightDp; } + // TODO(b/238331848): Consider simplifying logic that computes smallestScreenWidthDp. if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { // When entering to or exiting from Pip, the PipTaskOrganizer will set the @@ -2128,9 +2138,10 @@ class TaskFragment extends WindowContainer<WindowContainer> { // task, because they should not be affected by insets. inOutConfig.smallestScreenWidthDp = (int) (0.5f + Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density); - } else if (isEmbedded()) { - // For embedded TFs, the smallest width should be updated. Otherwise, inherit - // from the parent task would result in applications loaded wrong resource. + } else if (isEmbedded() || areBoundsLetterboxed || customContainerPolicy) { + // For embedded TFs and activities that are letteboxed or eligible for size + // compat mode, the smallest width should be updated. Otherwise, inherit from + // the parent task would result in applications loaded wrong resource. inOutConfig.smallestScreenWidthDp = Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 27d181fddb89..8b71a348108a 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -527,11 +527,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mService.mRootWindowContainer.forAllTasks((task) -> { boolean returnTask = !task.mCreatedByOrganizer; task.updateTaskOrganizerState(returnTask /* skipTaskAppeared */); - if (returnTask) { - SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task, + // It is possible for the task to not yet have a surface control, so ensure that + // the update succeeded in setting the organizer for the task before returning + if (task.isOrganized() && returnTask) { + SurfaceControl taskLeash = state.addTaskWithoutCallback(task, "TaskOrganizerController.registerTaskOrganizer"); - taskInfos.add( - new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl)); + taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), taskLeash)); } }); }; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 24a478e83f30..33b49823c620 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3756,6 +3756,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * hierarchy change implies a configuration change. */ private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) { + // Check if this is changing displays. If so, mark the old display as "ready" for + // transitions. This is to work around the problem where setting readiness against this + // container will only set the new display as ready and leave the old display as unready. + if (mSyncState != SYNC_STATE_NONE && oldParent != null + && oldParent.getDisplayContent() != null && (newParent == null + || oldParent.getDisplayContent() != newParent.getDisplayContent())) { + mTransitionController.setReady(oldParent.getDisplayContent()); + } + if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) { if (mSyncState == SYNC_STATE_NONE) { return; diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp index a6ed1ae56567..e724e804f4e7 100644 --- a/services/tests/apexsystemservices/Android.bp +++ b/services/tests/apexsystemservices/Android.bp @@ -39,6 +39,6 @@ java_test_host { ], test_suites: [ "device-tests", - "mts-core", + "mts-mainline-infra", ], } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java new file mode 100644 index 000000000000..d016d7e3e748 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java @@ -0,0 +1,112 @@ +/* + * 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.timedetector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.annotation.UserIdInt; +import android.app.time.ExternalTimeSuggestion; +import android.app.timedetector.GnssTimeSuggestion; +import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.NetworkTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; +import android.util.IndentingPrintWriter; + +/** + * A fake implementation of {@link com.android.server.timedetector.TimeDetectorStrategy} for use + * in tests. + */ +class FakeTimeDetectorStrategy implements TimeDetectorStrategy { + + // Call tracking. + private TelephonyTimeSuggestion mLastTelephonySuggestion; + private @UserIdInt Integer mLastManualSuggestionUserId; + private ManualTimeSuggestion mLastManualSuggestion; + private NetworkTimeSuggestion mLastNetworkSuggestion; + private GnssTimeSuggestion mLastGnssSuggestion; + private ExternalTimeSuggestion mLastExternalSuggestion; + private boolean mDumpCalled; + + @Override + public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) { + mLastTelephonySuggestion = timeSuggestion; + } + + @Override + public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion) { + mLastManualSuggestionUserId = userId; + mLastManualSuggestion = timeSuggestion; + return true; + } + + @Override + public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { + mLastNetworkSuggestion = timeSuggestion; + } + + @Override + public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) { + mLastGnssSuggestion = timeSuggestion; + } + + @Override + public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) { + mLastExternalSuggestion = timeSuggestion; + } + + @Override + public void dump(IndentingPrintWriter pw, String[] args) { + mDumpCalled = true; + } + + void resetCallTracking() { + mLastTelephonySuggestion = null; + mLastManualSuggestionUserId = null; + mLastManualSuggestion = null; + mLastNetworkSuggestion = null; + mLastGnssSuggestion = null; + mLastExternalSuggestion = null; + mDumpCalled = false; + } + + void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastTelephonySuggestion); + } + + void verifySuggestManualTimeCalled( + @UserIdInt int expectedUserId, ManualTimeSuggestion expectedSuggestion) { + assertEquals((Integer) expectedUserId, mLastManualSuggestionUserId); + assertEquals(expectedSuggestion, mLastManualSuggestion); + } + + void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastNetworkSuggestion); + } + + void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastGnssSuggestion); + } + + void verifySuggestExternalTimeCalled(ExternalTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastExternalSuggestion); + } + + void verifyDumpCalled() { + assertTrue(mDumpCalled); + } +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java new file mode 100644 index 000000000000..06512fbfed06 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java @@ -0,0 +1,68 @@ +/* + * 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.timedetector; + +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.os.HandlerThread; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.timezonedetector.TestHandler; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class TimeDetectorInternalImplTest { + + private Context mMockContext; + private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy; + + private TimeDetectorInternalImpl mTimeDetectorInternal; + private HandlerThread mHandlerThread; + private TestHandler mTestHandler; + + @Before + public void setUp() { + mMockContext = mock(Context.class); + + // Create a thread + handler for processing the work that the service posts. + mHandlerThread = new HandlerThread("TimeDetectorInternalTest"); + mHandlerThread.start(); + mTestHandler = new TestHandler(mHandlerThread.getLooper()); + + mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy(); + + mTimeDetectorInternal = new TimeDetectorInternalImpl( + mMockContext, mTestHandler, mFakeTimeDetectorStrategy); + } + + @Test + public void placeholder() { + // A placeholder test until there are real methods to test. + } + + @After + public void tearDown() throws Exception { + mHandlerThread.quit(); + mHandlerThread.join(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index e9617e9d973a..702ebeb3a4f9 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.timedetector; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -41,12 +42,14 @@ import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; +import android.app.timedetector.TimePoint; import android.content.Context; import android.content.pm.PackageManager; import android.os.HandlerThread; import android.os.IBinder; +import android.os.ParcelableException; import android.os.TimestampedValue; -import android.util.IndentingPrintWriter; +import android.util.NtpTrustedTime; import androidx.test.runner.AndroidJUnit4; @@ -77,7 +80,8 @@ public class TimeDetectorServiceTest { private TestHandler mTestHandler; private TestCallerIdentityInjector mTestCallerIdentityInjector; private FakeServiceConfigAccessor mFakeServiceConfigAccessor; - private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy; + private NtpTrustedTime mMockNtpTrustedTime; + private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy; @Before @@ -92,12 +96,13 @@ public class TimeDetectorServiceTest { mTestCallerIdentityInjector = new TestCallerIdentityInjector(); mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID); - mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy(); + mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy(); mFakeServiceConfigAccessor = new FakeServiceConfigAccessor(); + mMockNtpTrustedTime = mock(NtpTrustedTime.class); mTimeDetectorService = new TimeDetectorService( mMockContext, mTestHandler, mFakeServiceConfigAccessor, - mStubbedTimeDetectorStrategy, mTestCallerIdentityInjector); + mFakeTimeDetectorStrategy, mTestCallerIdentityInjector, mMockNtpTrustedTime); } @After @@ -275,7 +280,7 @@ public class TimeDetectorServiceTest { anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion); + mFakeTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion); } @Test(expected = SecurityException.class) @@ -301,7 +306,8 @@ public class TimeDetectorServiceTest { ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); assertTrue(mTimeDetectorService.suggestManualTime(manualTimeSuggestion)); - mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion); + mFakeTimeDetectorStrategy.verifySuggestManualTimeCalled( + mTestCallerIdentityInjector.getCallingUserId(), manualTimeSuggestion); verify(mMockContext).enforceCallingOrSelfPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), @@ -336,7 +342,7 @@ public class TimeDetectorServiceTest { eq(android.Manifest.permission.SET_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion); + mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion); } @Test(expected = SecurityException.class) @@ -366,7 +372,7 @@ public class TimeDetectorServiceTest { eq(android.Manifest.permission.SET_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion); + mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion); } @Test(expected = SecurityException.class) @@ -396,7 +402,24 @@ public class TimeDetectorServiceTest { eq(android.Manifest.permission.SUGGEST_EXTERNAL_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifySuggestExternalTimeCalled(externalTimeSuggestion); + mFakeTimeDetectorStrategy.verifySuggestExternalTimeCalled(externalTimeSuggestion); + } + + @Test + public void testLatestNetworkTime() { + NtpTrustedTime.TimeResult latestNetworkTime = + new NtpTrustedTime.TimeResult(1234L, 54321L, 999L); + when(mMockNtpTrustedTime.getCachedTimeResult()) + .thenReturn(latestNetworkTime); + TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(), + latestNetworkTime.getElapsedRealtimeMillis()); + assertEquals(expected, mTimeDetectorService.latestNetworkTime()); + } + + @Test + public void testLatestNetworkTime_noTimeAvailable() { + when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null); + assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime()); } @Test @@ -408,7 +431,7 @@ public class TimeDetectorServiceTest { mTimeDetectorService.dump(null, pw, null); verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP)); - mStubbedTimeDetectorStrategy.verifyDumpCalled(); + mFakeTimeDetectorStrategy.verifyDumpCalled(); } private static TimeConfiguration createTimeConfiguration(boolean autoDetectionEnabled) { @@ -455,79 +478,4 @@ public class TimeDetectorServiceTest { private static ExternalTimeSuggestion createExternalTimeSuggestion() { return new ExternalTimeSuggestion(100L, 1_000_000L); } - - private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { - - // Call tracking. - private TelephonyTimeSuggestion mLastTelephonySuggestion; - private ManualTimeSuggestion mLastManualSuggestion; - private NetworkTimeSuggestion mLastNetworkSuggestion; - private GnssTimeSuggestion mLastGnssSuggestion; - private ExternalTimeSuggestion mLastExternalSuggestion; - private boolean mDumpCalled; - - @Override - public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) { - mLastTelephonySuggestion = timeSuggestion; - } - - @Override - public boolean suggestManualTime(int userId, ManualTimeSuggestion timeSuggestion) { - mLastManualSuggestion = timeSuggestion; - return true; - } - - @Override - public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { - mLastNetworkSuggestion = timeSuggestion; - } - - @Override - public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) { - mLastGnssSuggestion = timeSuggestion; - } - - @Override - public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) { - mLastExternalSuggestion = timeSuggestion; - } - - @Override - public void dump(IndentingPrintWriter pw, String[] args) { - mDumpCalled = true; - } - - void resetCallTracking() { - mLastTelephonySuggestion = null; - mLastManualSuggestion = null; - mLastNetworkSuggestion = null; - mLastGnssSuggestion = null; - mLastExternalSuggestion = null; - mDumpCalled = false; - } - - void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastTelephonySuggestion); - } - - void verifySuggestManualTimeCalled(ManualTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastManualSuggestion); - } - - void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastNetworkSuggestion); - } - - void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastGnssSuggestion); - } - - void verifySuggestExternalTimeCalled(ExternalTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastExternalSuggestion); - } - - void verifyDumpCalled() { - assertTrue(mDumpCalled); - } - } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 4c7e8433b15b..d4886e440c41 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -183,7 +183,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -201,7 +202,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -214,7 +216,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -227,7 +230,7 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - 0, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET, 0, true, 10); } @Test @@ -240,7 +243,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - 0, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, + true, 10); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 324e244c46f5..f2640d2dc404 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1496,6 +1496,79 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testComputeConfigResourceOverrides_unresizableApp() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + + int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp; + int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp; + + // App should launch in fixed orientation letterbox. + // Activity bounds should be 700x1400 with the ratio as the display. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFitted(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertTrue(originalScreenWidthDp < originalScreenHeighthDp); + + // Rotate display to portrait. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + // After we rotate, the activity should go in the size-compat mode and report the same + // configuration values. + assertScaled(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + + // Restart activity + mActivity.restartProcessIfVisible(); + + // Now configuration should be updated + assertFitted(); + assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + assertEquals(mActivity.getConfiguration().screenWidthDp, + mActivity.getConfiguration().smallestScreenWidthDp); + } + + @Test + public void testComputeConfigResourceOverrides_resizableFixedOrientationActivity() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app without max aspect. + prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + + int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp; + int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp; + + // App should launch in fixed orientation letterbox. + // Activity bounds should be 700x1400 with the ratio as the display. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFitted(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertTrue(originalScreenWidthDp < originalScreenHeighthDp); + + // Rotate display to portrait. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + // Now configuration should be updated + assertFitted(); + assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + assertEquals(mActivity.getConfiguration().screenWidthDp, + mActivity.getConfiguration().smallestScreenWidthDp); + } + + @Test public void testSplitAspectRatioForUnresizablePortraitApps() { // Set up a display in landscape and ignoring orientation request. int screenWidth = 1600; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 46e21f1ffdbc..e2fe1b175dc8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -691,7 +691,8 @@ public class TaskTests extends WindowTestsBase { final ActivityRecord.CompatDisplayInsets compatInsets = new ActivityRecord.CompatDisplayInsets( display, activity, /* fixedOrientationBounds= */ null); - task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets); + task.computeConfigResourceOverrides( + inOutConfig, parentConfig, compatInsets, /* areBoundsLetterboxed */ true); assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 9c2aac066084..540741217ae8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -459,6 +459,23 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Test + public void testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl() + throws RemoteException { + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final Task rootTask2 = createRootTask(); + final Task task2 = createTask(rootTask2); + rootTask2.setSurfaceControl(null); + ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); + final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + assertContainsTasks(existingTasks, rootTask); + + // Verify we don't get onTaskAppeared if we are returned the tasks + verify(organizer, never()) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); + } + + @Test public void testTaskTransaction() { removeGlobalMinSizeRestriction(); final Task rootTask = new TaskBuilder(mSupervisor) diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 7731e098d9f5..855d3c1f4ea7 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -46,6 +46,7 @@ android_test { "launcher-helper-lib", "launcher-aosp-tapl", "platform-test-annotations", + "wm-flicker-window-extensions", ], } @@ -83,5 +84,21 @@ java_library { "flickertestapplib", "truth-prebuilt", "app-helpers-core", + "wm-flicker-window-extensions", ], } + +android_library_import { + name: "wm-flicker-window-extensions_nodeps", + aars: ["libs/window-extensions-release.aar"], + sdk_version: "current", +} + +java_library { + name: "wm-flicker-window-extensions", + sdk_version: "current", + static_libs: [ + "wm-flicker-window-extensions_nodeps", + ], + installable: false, +} diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml index fda609196456..e173eba0a393 100644 --- a/tests/FlickerTests/AndroidManifest.xml +++ b/tests/FlickerTests/AndroidManifest.xml @@ -43,6 +43,7 @@ <!-- Allow the test to write directly to /sdcard/ --> <application android:requestLegacyExternalStorage="true"> <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" android:required="false"/> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/FlickerTests/libs/window-extensions-release.aar b/tests/FlickerTests/libs/window-extensions-release.aar Binary files differnew file mode 100644 index 000000000000..6fc9a67fb19f --- /dev/null +++ b/tests/FlickerTests/libs/window-extensions-release.aar diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt new file mode 100644 index 000000000000..ed411b5eaf02 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt @@ -0,0 +1,33 @@ +/* + * 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.wm.flicker.activityembedding + +import android.app.Instrumentation +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.Before + +abstract class ActivityEmbeddingTestBase { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = ActivityEmbeddingAppHelper(instrumentation) + + @Before + fun assumeActivityEmbeddingSupported() { + // The test should only be run on devices that support ActivityEmbedding. + ActivityEmbeddingAppHelper.assumeActivityEmbeddingSupportedDevice() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt new file mode 100644 index 000000000000..28a72f4da592 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt @@ -0,0 +1,114 @@ +/* + * 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.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.view.Surface +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test opening an activity that will launch another activity as ActivityEmbedding placeholder in + * split. + * + * To run this test: `atest FlickerTests:OpenActivityEmbeddingPlaceholderSplit` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenActivityEmbeddingPlaceholderSplit(private val testSpec: FlickerTestParameter) : + ActivityEmbeddingTestBase() { + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + } + } + transitions { + testApp.launchPlaceholderSplit(wmHelper) + } + teardown { + test { + device.pressHome() + testApp.exit(wmHelper) + } + } + } + } + + @Presubmit + @Test + fun mainActivityBecomesInvisible() { + testSpec.assertLayers { + isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + } + + @Presubmit + @Test + fun placeholderSplitBecomesVisible() { + testSpec.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + testSpec.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring + * repetitions, screen orientation and navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + repetitions = 1, + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt new file mode 100644 index 000000000000..a01f633cbae4 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -0,0 +1,97 @@ +/* + * 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.wm.flicker.helpers + +import android.app.Instrumentation +import android.support.test.launcherhelper.ILauncherStrategy +import android.support.test.launcherhelper.LauncherStrategyFactory +import android.util.Log +import androidx.test.uiautomator.By +import androidx.test.uiautomator.Until +import androidx.window.extensions.WindowExtensions +import androidx.window.extensions.WindowExtensionsProvider +import androidx.window.extensions.embedding.ActivityEmbeddingComponent +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.traces.common.FlickerComponentName +import com.android.server.wm.traces.common.windowmanager.WindowManagerState.Companion.STATE_RESUMED +import com.android.server.wm.traces.parser.toFlickerComponent +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import org.junit.Assume.assumeNotNull + +class ActivityEmbeddingAppHelper @JvmOverloads constructor( + instr: Instrumentation, + launcherName: String = ActivityOptions.ACTIVITY_EMBEDDING_LAUNCHER_NAME, + component: FlickerComponentName = MAIN_ACTIVITY_COMPONENT, + launcherStrategy: ILauncherStrategy = LauncherStrategyFactory + .getInstance(instr) + .launcherStrategy +) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { + + /** + * Clicks the button to launch the placeholder primary activity, which should launch the + * placeholder secondary activity based on the placeholder rule. + */ + fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) { + val launchButton = uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")), + FIND_TIMEOUT) + require(launchButton != null) { + "Can't find launch placeholder split button on screen." + } + launchButton.click() + wmHelper.StateSyncBuilder() + .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, STATE_RESUMED) + .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, STATE_RESUMED) + .waitForAndVerify() + } + + companion object { + private const val TAG = "ActivityEmbeddingAppHelper" + + val MAIN_ACTIVITY_COMPONENT = ActivityOptions + .ACTIVITY_EMBEDDING_MAIN_ACTIVITY_COMPONENT_NAME.toFlickerComponent() + + val PLACEHOLDER_PRIMARY_COMPONENT = ActivityOptions + .ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME.toFlickerComponent() + + val PLACEHOLDER_SECONDARY_COMPONENT = ActivityOptions + .ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME + .toFlickerComponent() + + @JvmStatic + fun getWindowExtensions(): WindowExtensions? { + try { + return WindowExtensionsProvider.getWindowExtensions() + } catch (e: NoClassDefFoundError) { + Log.d(TAG, "Extension implementation not found") + } catch (e: UnsupportedOperationException) { + Log.d(TAG, "Stub Extension") + } + return null + } + + @JvmStatic + fun getActivityEmbeddingComponent(): ActivityEmbeddingComponent? { + return getWindowExtensions()?.activityEmbeddingComponent + } + + @JvmStatic + fun assumeActivityEmbeddingSupportedDevice() { + assumeNotNull(getActivityEmbeddingComponent()) + } + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp index 78660c04d8d4..0c698abeceeb 100644 --- a/tests/FlickerTests/test-apps/flickerapp/Android.bp +++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp @@ -26,6 +26,11 @@ android_test { srcs: ["**/*.java"], sdk_version: "current", test_suites: ["device-tests"], + static_libs: [ + "androidx.test.ext.junit", + "wm-flicker-common-app-helpers", + "wm-flicker-window-extensions", + ], } java_library { diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 3e2130dc480f..387f19b2396f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -15,12 +15,14 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.wm.flicker.testapp"> + package="com.android.server.wm.flicker.testapp"> <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> <application android:allowBackup="false" android:supportsRtl="true"> + <uses-library android:name="androidx.window.extensions" android:required="false"/> + <activity android:name=".SimpleActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity" android:theme="@style/CutoutShortEdges" @@ -163,5 +165,33 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + + <activity + android:name=".ActivityEmbeddingMainActivity" + android:label="ActivityEmbedding Main" + android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" + android:theme="@style/CutoutShortEdges" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".ActivityEmbeddingPlaceholderPrimaryActivity" + android:label="ActivityEmbedding Placeholder Primary" + android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" + android:theme="@style/CutoutShortEdges" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" + android:exported="false"> + </activity> + <activity + android:name=".ActivityEmbeddingPlaceholderSecondaryActivity" + android:label="ActivityEmbedding Placeholder Secondary" + android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" + android:theme="@style/CutoutShortEdges" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" + android:exported="false"/> </application> </manifest> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml new file mode 100644 index 000000000000..3a02cadc90dd --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root_activity_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml new file mode 100644 index 000000000000..19c81a8ba0f5 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:background="@android:color/holo_orange_light"> + + <Button + android:id="@+id/launch_placeholder_split_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:layout_centerHorizontal="true" + android:onClick="launchPlaceholderSplit" + android:text="Launch Placeholder Split" /> + +</LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java new file mode 100644 index 000000000000..cd23e9f76ebf --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java @@ -0,0 +1,34 @@ +/* + * 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.wm.flicker.testapp; + +import android.app.Activity; +import android.os.Bundle; + +/** Base activity of ActivityEmbedding split activities. */ +public abstract class ActivityEmbeddingBaseActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_embedding_base_layout); + findViewById(R.id.root_activity_layout).setBackgroundColor(getBackgroundColor()); + } + + /** Sets different colors to visually distinguish split pairs. */ + abstract int getBackgroundColor(); +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java new file mode 100644 index 000000000000..166e3ca2a156 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java @@ -0,0 +1,85 @@ +/* + * 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.wm.flicker.testapp; + +import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME; +import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.ArraySet; +import android.util.Log; +import android.view.View; + +import androidx.window.extensions.embedding.ActivityEmbeddingComponent; +import androidx.window.extensions.embedding.EmbeddingRule; +import androidx.window.extensions.embedding.SplitPlaceholderRule; + +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; + +import java.util.Set; + +/** Main activity of the ActivityEmbedding test app to launch other embedding activities. */ +public class ActivityEmbeddingMainActivity extends Activity { + private static final String TAG = "ActivityEmbeddingMainActivity"; + private static final float DEFAULT_SPLIT_RATIO = 0.5f; + + private ActivityEmbeddingComponent mEmbeddingComponent; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_embedding_main_layout); + + initializeSplitRules(); + } + + /** R.id.launch_placeholder_split_button onClick */ + public void launchPlaceholderSplit(View view) { + startActivity(new Intent().setComponent( + ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME)); + } + + private void initializeSplitRules() { + mEmbeddingComponent = ActivityEmbeddingAppHelper.getActivityEmbeddingComponent(); + if (mEmbeddingComponent == null) { + // Embedding not supported + Log.d(TAG, "ActivityEmbedding is not supported on this device"); + finish(); + return; + } + + mEmbeddingComponent.setEmbeddingRules(getSplitRules()); + } + + private Set<EmbeddingRule> getSplitRules() { + final Set<EmbeddingRule> rules = new ArraySet<>(); + + final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder( + new Intent().setComponent( + ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME), + activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity, + intent -> intent.getComponent().equals( + ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME), + windowMetrics -> true) + .setSplitRatio(DEFAULT_SPLIT_RATIO) + .build(); + rules.add(placeholderRule); + return rules; + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java new file mode 100644 index 000000000000..05c7a0b34c33 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java @@ -0,0 +1,30 @@ +/* + * 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.wm.flicker.testapp; + +import android.graphics.Color; + +/** + * Primary activity that will launch {@link ActivityEmbeddingPlaceholderSecondaryActivity} to split + * as placeholder based on the placeholder rule. + */ +public class ActivityEmbeddingPlaceholderPrimaryActivity extends ActivityEmbeddingBaseActivity { + @Override + int getBackgroundColor() { + return Color.BLUE; + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java new file mode 100644 index 000000000000..a9a51cd9ad8b --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java @@ -0,0 +1,30 @@ +/* + * 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.wm.flicker.testapp; + +import android.graphics.Color; + +/** + * Activity to be used as the secondary placeholder activity to split with + * {@link ActivityEmbeddingPlaceholderPrimaryActivity}. + */ +public class ActivityEmbeddingPlaceholderSecondaryActivity extends ActivityEmbeddingBaseActivity { + @Override + int getBackgroundColor() { + return Color.GREEN; + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 6cda482dd30a..19fafb7d2e95 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -87,4 +87,17 @@ public class ActivityOptions { public static final ComponentName NOTIFICATION_ACTIVITY_COMPONENT_NAME = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".NotificationActivity"); + + public static final String ACTIVITY_EMBEDDING_LAUNCHER_NAME = "ActivityEmbeddingMainActivity"; + public static final ComponentName ACTIVITY_EMBEDDING_MAIN_ACTIVITY_COMPONENT_NAME = + new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ActivityEmbeddingMainActivity"); + public static final ComponentName + ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME = new ComponentName( + FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity"); + public static final ComponentName + ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME = new ComponentName( + FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity"); } diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java index 782439f80fc8..d133f6fbdd87 100644 --- a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java +++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java @@ -117,6 +117,7 @@ public class UsbManagerTestLib { testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP); testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI); testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS); + testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM); } public void testSetCurrentFunctions_shouldMatched() { @@ -125,5 +126,6 @@ public class UsbManagerTestLib { testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP); testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI); testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS); + testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM); } } diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java index 861d221238ff..86bcb7290d95 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -182,6 +182,14 @@ public class UsbHandlerTest { @SmallTest @Test + public void setFunctionsNcm() { + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_NCM)); + assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_NCM, 0); + } + + @SmallTest + @Test public void setFunctionsNcmAndRndis() { final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM; |