diff options
185 files changed, 3880 insertions, 1882 deletions
diff --git a/Android.bp b/Android.bp index 9690969305bf..ee5e992935ae 100644 --- a/Android.bp +++ b/Android.bp @@ -917,7 +917,6 @@ filegroup { "core/java/com/android/internal/util/RingBufferIndices.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", - "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/com/android/internal/util/TokenBucket.java", ], @@ -944,7 +943,6 @@ filegroup { "core/java/com/android/internal/util/MessageUtils.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", - "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", ], } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 6967d819a448..4c8ab9385903 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -59,15 +59,6 @@ import java.util.Objects; * constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an * exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is * valid to schedule jobs with no constraints. - * <p> Prior to Android version {@link Build.VERSION_CODES#S}, jobs could only have a maximum of 100 - * jobs scheduled at a time. Starting with Android version {@link Build.VERSION_CODES#S}, that limit - * has been increased to 150. Expedited jobs also count towards the limit. - * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time - * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with - * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes. - * Starting from Android version {@link Build.VERSION_CODES#S}, jobs will still be stopped after - * 10 minutes if the system is busy or needs the resources, but if not, jobs may continue running - * longer than 10 minutes. */ public class JobInfo implements Parcelable { private static String TAG = "JobInfo"; @@ -1471,7 +1462,7 @@ public class JobInfo implements Parcelable { * <ol> * <li>Run as soon as possible</li> * <li>Be less restricted during Doze and battery saver</li> - * <li>Have network access</li> + * <li>Have the same network access as foreground services</li> * <li>Be less likely to be killed than regular jobs</li> * <li>Be subject to background location throttling</li> * </ol> diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index 361325dae7fd..1f4ef0470ebd 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -57,6 +57,19 @@ import java.util.List; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}. * + * <p> Prior to Android version {@link android.os.Build.VERSION_CODES#S}, jobs could only have + * a maximum of 100 jobs scheduled at a time. Starting with Android version + * {@link android.os.Build.VERSION_CODES#S}, that limit has been increased to 150. + * Expedited jobs also count towards the limit. + * + * <p> In Android version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum + * execution time of one minute. Starting with Android version + * {@link android.os.Build.VERSION_CODES#M} and ending with Android version + * {@link android.os.Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes. + * Starting from Android version {@link android.os.Build.VERSION_CODES#S}, jobs will still be + * stopped after 10 minutes if the system is busy or needs the resources, but if not, jobs + * may continue running longer than 10 minutes. + * * <p class="caution"><strong>Note:</strong> Beginning with API 30 * ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications. * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java index 61afadab9b0c..0f3d299291c5 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobService.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java @@ -74,6 +74,7 @@ public abstract class JobService extends Service { /** * Call this to inform the JobScheduler that the job has finished its work. When the * system receives this message, it releases the wakelock being held for the job. + * This does not need to be called if {@link #onStopJob(JobParameters)} has been called. * <p> * You can request that the job be scheduled again by passing {@code true} as * the <code>wantsReschedule</code> parameter. This will apply back-off policy @@ -135,6 +136,8 @@ public abstract class JobService extends Service { /** * This method is called if the system has determined that you must stop execution of your job * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}. + * Once this method is called, you no longer need to call + * {@link #jobFinished(JobParameters, boolean)}. * * <p>This will happen if the requirements specified at schedule time are no longer met. For * example you may have requested WiFi with @@ -144,8 +147,8 @@ public abstract class JobService extends Service { * idle maintenance window. You are solely responsible for the behavior of your application * upon receipt of this message; your app will likely start to misbehave if you ignore it. * <p> - * Once this method returns, the system releases the wakelock that it is holding on - * behalf of the job.</p> + * Once this method returns (or times out), the system releases the wakelock that it is holding + * on behalf of the job.</p> * * @param params The parameters identifying this job, as supplied to * the job in the {@link #onStartJob(JobParameters)} callback. diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index 35159763332e..88f21a53f930 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -174,6 +174,8 @@ public class PowerExemptionManager { public static final int REASON_ALLOWLISTED_PACKAGE = 65; /** @hide */ public static final int REASON_APPOP = 66; + /** @hide */ + public static final int REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD = 67; /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list. Reason code for temp and system allow list starts here. @@ -334,6 +336,7 @@ public class PowerExemptionManager { REASON_EXEMPTED_PACKAGE, REASON_ALLOWLISTED_PACKAGE, REASON_APPOP, + REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD, // temp and system allow list reasons. REASON_GEOFENCING, REASON_PUSH_MESSAGING, @@ -585,6 +588,8 @@ public class PowerExemptionManager { return "ALLOWLISTED_PACKAGE"; case REASON_APPOP: return "APPOP"; + case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD: + return "ACTIVITY_VISIBILITY_GRACE_PERIOD"; case REASON_GEOFENCING: return "GEOFENCING"; case REASON_PUSH_MESSAGING: diff --git a/api/Android.bp b/api/Android.bp index 1fdf1771bb13..4baf7c1d5836 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -351,6 +351,7 @@ genrule { genrule { name: "services-system-server-current.txt", srcs: [ + ":service-media-s{.system-server.api.txt}", ":service-permission{.system-server.api.txt}", ":non-updatable-system-server-current.txt", ], @@ -374,6 +375,7 @@ genrule { genrule { name: "services-system-server-removed.txt", srcs: [ + ":service-media-s{.system-server.removed-api.txt}", ":service-permission{.system-server.removed-api.txt}", ":non-updatable-system-server-removed.txt", ], diff --git a/core/api/current.txt b/core/api/current.txt index 97f98553ed7c..2b5196b3b5f7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -99,6 +99,7 @@ package android { field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE"; + field public static final String MANAGE_MEDIA = "android.permission.MANAGE_MEDIA"; field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS"; field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS"; field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; @@ -40377,6 +40378,7 @@ package android.telephony { field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; field public static final String KEY_CARRIER_NR_AVAILABILITY_INT = "carrier_nr_availability_int"; + field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool"; field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; @@ -52401,17 +52403,6 @@ package android.view.translation { method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest); } - public final class UiTranslationManager { - method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback); - method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback); - } - - public interface UiTranslationStateCallback { - method public void onFinished(); - method public void onPaused(); - method public void onStarted(@NonNull String, @NonNull String); - } - public final class ViewTranslationRequest implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.view.autofill.AutofillId getAutofillId(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1d1303f49a90..54c666314f98 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2096,6 +2096,7 @@ package android.bluetooth { field public static final int LE_FLAG_SIMULTANEOUS_CONTROLLER = 3; // 0x3 field public static final int LE_FLAG_SIMULTANEOUS_HOST = 4; // 0x4 field public static final int LE_TK_OCTETS = 16; // 0x10 + field public static final int OOB_LENGTH_OCTETS = 2; // 0x2 field public static final int RANDOMIZER_OCTETS = 16; // 0x10 } @@ -10867,7 +10868,14 @@ package android.telephony { method @NonNull public static android.os.PersistableBundle getDefaultConfig(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String); + field public static final int GBA_DIGEST = 3; // 0x3 + field public static final int GBA_ME = 1; // 0x1 + field public static final int GBA_U = 2; // 0x2 field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string"; + field public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int"; + field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int"; + field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int"; field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool"; } @@ -11025,6 +11033,19 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ImsiEncryptionInfo> CREATOR; } + public final class LinkCapacityEstimate implements android.os.Parcelable { + ctor public LinkCapacityEstimate(int, int, int); + method public int describeContents(); + method public int getDownlinkCapacityKbps(); + method public int getType(); + method public int getUplinkCapacityKbps(); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR; + field public static final int INVALID = -1; // 0xffffffff + field public static final int LCE_TYPE_COMBINED = 2; // 0x2 + field public static final int LCE_TYPE_PRIMARY = 0; // 0x0 + field public static final int LCE_TYPE_SECONDARY = 1; // 0x1 + } + public final class LteVopsSupportInfo implements android.os.Parcelable { ctor public LteVopsSupportInfo(int, int); method public int describeContents(); @@ -11537,6 +11558,7 @@ package android.telephony { field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; // 0x25 field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d @@ -11567,6 +11589,10 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); } + public static interface TelephonyCallback.LinkCapacityEstimateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>); + } + public static interface TelephonyCallback.OutgoingEmergencyCallListener { method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); } @@ -11638,7 +11664,6 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierBandwidth getCarrierBandwidth(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d4b3f16cedb5..e486fa2c2e29 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -730,6 +730,11 @@ package android.content.pm { method public static boolean isTranslucentOrFloating(android.content.res.TypedArray); field public static final long FORCE_NON_RESIZE_APP = 181136395L; // 0xacbec0bL field public static final long FORCE_RESIZE_APP = 174042936L; // 0xa5faf38L + field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L + field public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // 0xabf9183L + field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f; + field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL + field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f; field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2 } diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index fbabfac706e1..633b986c06c9 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -25,6 +25,7 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.util.Singleton; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.policy.IKeyguardDismissCallback; @@ -104,12 +105,9 @@ public class ActivityClient { } } - void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { + void reportSizeConfigurations(IBinder token, SizeConfigurationBuckets sizeConfigurations) { try { - getActivityClientController().reportSizeConfigurations(token, - horizontalSizeConfiguration, verticalSizeConfigurations, - smallestSizeConfigurations); + getActivityClientController().reportSizeConfigurations(token, sizeConfigurations); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8977ba774d0b..837154fd6080 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -158,7 +158,6 @@ import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; import android.util.SuperNotCalledException; import android.util.UtilConfig; import android.util.proto.ProtoOutputStream; @@ -180,6 +179,7 @@ import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.IContentCaptureOptionsCallback; import android.view.translation.TranslationSpec; import android.webkit.WebView; +import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; import android.window.SplashScreenView; @@ -604,6 +604,8 @@ public final class ActivityThread extends ClientTransactionHandler @LifecycleState private int mLifecycleState = PRE_ON_CREATE; + private SizeConfigurationBuckets mSizeConfigurations; + @VisibleForTesting @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public ActivityClientRecord() { @@ -3764,23 +3766,8 @@ public final class ActivityThread extends ClientTransactionHandler if (configurations == null) { return; } - SparseIntArray horizontal = new SparseIntArray(); - SparseIntArray vertical = new SparseIntArray(); - SparseIntArray smallest = new SparseIntArray(); - for (int i = configurations.length - 1; i >= 0; i--) { - Configuration config = configurations[i]; - if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - vertical.put(config.screenHeightDp, 0); - } - if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - horizontal.put(config.screenWidthDp, 0); - } - if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - smallest.put(config.smallestScreenWidthDp, 0); - } - } - ActivityClient.getInstance().reportSizeConfigurations(r.token, horizontal.copyKeys(), - vertical.copyKeys(), smallest.copyKeys()); + r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); + ActivityClient.getInstance().reportSizeConfigurations(r.token, r.mSizeConfigurations); } private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { @@ -5773,7 +5760,10 @@ public final class ActivityThread extends ClientTransactionHandler // onConfigurationChanged. // TODO(b/173090263): Use diff instead after the improvement of AssetManager and // ResourcesImpl constructions. - final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); + int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); + final ActivityClientRecord cr = getActivityClient(activityToken); + diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig, + cr != null ? cr.mSizeConfigurations : null); if (diff == 0) { if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d310e8f0ef5c..1c487e5fc6e1 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -90,6 +90,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -1207,9 +1208,21 @@ public class AppOpsManager { */ public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE; + /** + * Allow apps to create the requests to manage the media files without user confirmation. + * + * @see android.Manifest.permission#MANAGE_MEDIA + * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection) + * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean) + * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection) + * + * @hide + */ + public static final int OP_MANAGE_MEDIA = AppProtoEnums.APP_OP_MANAGE_MEDIA; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 110; + public static final int _NUM_OP = 111; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1607,6 +1620,18 @@ public class AppOpsManager { */ public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source"; + /** + * Allow apps to create the requests to manage the media files without user confirmation. + * + * @see android.Manifest.permission#MANAGE_MEDIA + * @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection) + * @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean) + * @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection) + * + * @hide + */ + public static final String OPSTR_MANAGE_MEDIA = "android:manage_media"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -1688,6 +1713,7 @@ public class AppOpsManager { OP_MANAGE_ONGOING_CALLS, OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, OP_SCHEDULE_EXACT_ALARM, + OP_MANAGE_MEDIA, }; /** @@ -1809,6 +1835,7 @@ public class AppOpsManager { OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE + OP_MANAGE_MEDIA, // MANAGE_MEDIA }; /** @@ -1925,6 +1952,7 @@ public class AppOpsManager { OPSTR_SCHEDULE_EXACT_ALARM, OPSTR_FINE_LOCATION_SOURCE, OPSTR_COARSE_LOCATION_SOURCE, + OPSTR_MANAGE_MEDIA, }; /** @@ -2042,6 +2070,7 @@ public class AppOpsManager { "SCHEDULE_EXACT_ALARM", "FINE_LOCATION_SOURCE", "COARSE_LOCATION_SOURCE", + "MANAGE_MEDIA", }; /** @@ -2160,6 +2189,7 @@ public class AppOpsManager { Manifest.permission.SCHEDULE_EXACT_ALARM, null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE, null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE, + Manifest.permission.MANAGE_MEDIA, }; /** @@ -2278,6 +2308,7 @@ public class AppOpsManager { null, // SCHEDULE_EXACT_ALARM null, // ACCESS_FINE_LOCATION_SOURCE null, // ACCESS_COARSE_LOCATION_SOURCE + null, // MANAGE_MEDIA }; /** @@ -2395,6 +2426,7 @@ public class AppOpsManager { null, // SCHEDULE_EXACT_ALARM null, // ACCESS_FINE_LOCATION_SOURCE null, // ACCESS_COARSE_LOCATION_SOURCE + null, // MANAGE_MEDIA }; /** @@ -2511,6 +2543,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE + AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA }; /** @@ -2631,6 +2664,7 @@ public class AppOpsManager { false, // SCHEDULE_EXACT_ALARM false, // ACCESS_FINE_LOCATION_SOURCE false, // ACCESS_COARSE_LOCATION_SOURCE + false, // MANAGE_MEDIA }; /** diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 573931ed228e..ed4836e31209 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.PersistableBundle; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.policy.IKeyguardDismissCallback; @@ -49,8 +50,8 @@ interface IActivityClientController { oneway void activityDestroyed(in IBinder token); oneway void activityRelaunched(in IBinder token); - oneway void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration, - in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); + oneway void reportSizeConfigurations(in IBinder token, + in SizeConfigurationBuckets sizeConfigurations); boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 5402381b7207..e83557c18f8f 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -167,7 +167,8 @@ interface IWallpaperManager { * @hide */ void removeOnLocalColorsChangedListener( - in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId); + in ILocalWallpaperColorConsumer callback, in List<RectF> area, + int which, int userId, int displayId); /** * @hide diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7dbbc54665e9..3ef6757ade60 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -68,6 +68,7 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.view.Display; @@ -318,8 +319,20 @@ public class WallpaperManager { private int mCachedWallpaperUserId; private Bitmap mDefaultWallpaper; private Handler mMainLooperHandler; - private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer> - mLocalColorCallbacks = new ArrayMap<>(); + private ArrayMap<RectF, ArraySet<LocalWallpaperColorConsumer>> mLocalColorAreas = + new ArrayMap<>(); + private ILocalWallpaperColorConsumer mLocalColorCallback = + new ILocalWallpaperColorConsumer.Stub() { + @Override + public void onColorsChanged(RectF area, WallpaperColors colors) { + ArraySet<LocalWallpaperColorConsumer> callbacks = + mLocalColorAreas.get(area); + if (callbacks == null) return; + for (LocalWallpaperColorConsumer callback: callbacks) { + callback.onColorsChanged(area, colors); + } + } + }; Globals(IWallpaperManager service, Looper looper) { mService = service; @@ -361,37 +374,46 @@ public class WallpaperManager { } } - private ILocalWallpaperColorConsumer wrap(LocalWallpaperColorConsumer callback) { - ILocalWallpaperColorConsumer callback2 = new ILocalWallpaperColorConsumer.Stub() { - @Override - public void onColorsChanged(RectF area, WallpaperColors colors) { - callback.onColorsChanged(area, colors); - } - }; - mLocalColorCallbacks.put(callback, callback2); - return callback2; - } - public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, @NonNull List<RectF> regions, int which, int userId, int displayId) { + for (RectF area: regions) { + ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area); + if (callbacks == null) { + callbacks = new ArraySet<>(); + mLocalColorAreas.put(area, callbacks); + } + callbacks.add(callback); + } try { - mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which, + mService.addOnLocalColorsChangedListener(mLocalColorCallback , regions, which, userId, displayId); } catch (RemoteException e) { // Can't get colors, connection lost. + Log.e(TAG, "Can't register for local color updates", e); } } public void removeOnColorsChangedListener( @NonNull LocalWallpaperColorConsumer callback, int which, int userId, int displayId) { - ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback); - if (callback2 == null) return; + final ArrayList<RectF> removeAreas = new ArrayList<>(); + for (RectF area : mLocalColorAreas.keySet()) { + ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area); + if (callbacks == null) continue; + callbacks.remove(callback); + if (callbacks.size() == 0) { + mLocalColorAreas.remove(area); + removeAreas.add(area); + } + } try { - mService.removeOnLocalColorsChangedListener( - callback2, which, userId, displayId); + if (removeAreas.size() > 0) { + mService.removeOnLocalColorsChangedListener( + mLocalColorCallback, removeAreas, which, userId, displayId); + } } catch (RemoteException e) { // Can't get colors, connection lost. + Log.e(TAG, "Can't unregister for local color updates", e); } } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 94ab0dd00113..9f8fcc15b747 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -253,7 +253,7 @@ public class LaunchActivityItem extends ClientTransactionItem { return other == null; } return other != null && mInfo.flags == other.flags - && mInfo.maxAspectRatio == other.maxAspectRatio + && mInfo.getMaxAspectRatio() == other.getMaxAspectRatio() && Objects.equals(mInfo.launchToken, other.launchToken) && Objects.equals(mInfo.getComponentName(), other.getComponentName()); } diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java index 98107461fb4c..08d694eb93e2 100644 --- a/core/java/android/bluetooth/OobData.java +++ b/core/java/android/bluetooth/OobData.java @@ -76,7 +76,7 @@ public final class OobData implements Parcelable { private static final String TAG = "OobData"; /** The {@link OobData#mClassicLength} may be. (AD 3.1.1) (CSS 1.6.2) @hide */ @SystemApi - private static final int OOB_LENGTH_OCTETS = 2; + public static final int OOB_LENGTH_OCTETS = 2; /** * The length for the {@link OobData#mDeviceAddressWithType}(6) and Address Type(1). * (AD 3.1.2) (CSS 1.6.2) @@ -590,7 +590,6 @@ public final class OobData implements Parcelable { * * @hide */ - @SystemApi private ClassicBuilder(@NonNull byte[] confirmationHash, @NonNull byte[] classicLength, @NonNull byte[] deviceAddressWithType) { Preconditions.checkNotNull(confirmationHash); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 58f83a73ff16..feb58a30e519 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -21,6 +21,7 @@ import android.annotation.TestApi; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.compat.annotation.Overridable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; @@ -254,7 +255,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @See {@link android.R.attr#maxAspectRatio}. * @hide */ - public float maxAspectRatio; + private float mMaxAspectRatio; /** * Value indicating the minimum aspect ratio the activity supports. @@ -263,7 +264,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @See {@link android.R.attr#minAspectRatio}. * @hide */ - public float minAspectRatio; + private float mMinAspectRatio; /** * Indicates that the activity works well with size changes like display changing size. @@ -948,6 +949,57 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public @interface SizeChangesSupportMode {} /** + * This change id is the gatekeeper for all treatments that force a given min aspect ratio. + * Enabling this change will allow the following min aspect ratio treatments to be applied: + * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM + * OVERRIDE_MIN_ASPECT_RATIO_LARGE + * + * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's + * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's + * manifest value is higher. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id + + /** + * This change id sets the activity's min aspect ratio to a medium value as defined by + * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE. + * + * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // buganizer id + + /** @hide Medium override aspect ratio, currently 3:2. */ + @TestApi + public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 3 / 2f; + + /** + * This change id sets the activity's min aspect ratio to a large value as defined by + * OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE. + * + * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // buganizer id + + /** @hide Large override aspect ratio, currently 16:9 */ + @TestApi + public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f; + + /** * Convert Java change bits to native. * * @hide @@ -1118,8 +1170,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { requestedVrComponent = orig.requestedVrComponent; rotationAnimation = orig.rotationAnimation; colorMode = orig.colorMode; - maxAspectRatio = orig.maxAspectRatio; - minAspectRatio = orig.minAspectRatio; + mMaxAspectRatio = orig.mMaxAspectRatio; + mMinAspectRatio = orig.mMinAspectRatio; supportsSizeChanges = orig.supportsSizeChanges; attributionTags = orig.attributionTags; } @@ -1149,7 +1201,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @hide */ public boolean hasFixedAspectRatio() { - return maxAspectRatio != 0 || minAspectRatio != 0; + return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0; } /** @@ -1262,6 +1314,58 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** @hide */ + public void setMaxAspectRatio(float maxAspectRatio) { + this.mMaxAspectRatio = maxAspectRatio; + } + + /** @hide */ + public float getMaxAspectRatio() { + return mMaxAspectRatio; + } + + /** @hide */ + public void setMinAspectRatio(float minAspectRatio) { + this.mMinAspectRatio = minAspectRatio; + } + + /** + * Returns the min aspect ratio of this activity. + * + * This takes into account the minimum aspect ratio as defined in the app's manifest and + * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO. + * + * In the rare cases where the manifest minimum aspect ratio is required, use + * {@code getManifestMinAspectRatio}. + * @hide + */ + public float getMinAspectRatio() { + if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return mMinAspectRatio; + } + + if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio); + } + + if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM, + applicationInfo.packageName, + UserHandle.getUserHandleForUid(applicationInfo.uid))) { + return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio); + } + + return mMinAspectRatio; + } + + /** @hide */ + public float getManifestMinAspectRatio() { + return mMinAspectRatio; + } + + /** @hide */ @UnsupportedAppUsage public static boolean isResizeableMode(int mode) { return mode == RESIZE_MODE_RESIZEABLE @@ -1360,11 +1464,14 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (requestedVrComponent != null) { pw.println(prefix + "requestedVrComponent=" + requestedVrComponent); } - if (maxAspectRatio != 0) { - pw.println(prefix + "maxAspectRatio=" + maxAspectRatio); + if (getMaxAspectRatio() != 0) { + pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio()); } - if (minAspectRatio != 0) { - pw.println(prefix + "minAspectRatio=" + minAspectRatio); + if (getMinAspectRatio() != 0) { + pw.println(prefix + "minAspectRatio=" + getMinAspectRatio()); + if (getManifestMinAspectRatio() != getMinAspectRatio()) { + pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio()); + } } if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); @@ -1420,8 +1527,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeString8(requestedVrComponent); dest.writeInt(rotationAnimation); dest.writeInt(colorMode); - dest.writeFloat(maxAspectRatio); - dest.writeFloat(minAspectRatio); + dest.writeFloat(mMaxAspectRatio); + dest.writeFloat(mMinAspectRatio); dest.writeBoolean(supportsSizeChanges); dest.writeString8Array(attributionTags); } @@ -1540,8 +1647,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { requestedVrComponent = source.readString8(); rotationAnimation = source.readInt(); colorMode = source.readInt(); - maxAspectRatio = source.readFloat(); - minAspectRatio = source.readFloat(); + mMaxAspectRatio = source.readFloat(); + mMinAspectRatio = source.readFloat(); supportsSizeChanges = source.readBoolean(); attributionTags = source.createString8Array(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5e08399f8abb..5ff11240db72 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4890,8 +4890,8 @@ public class PackageParser { info.maxRecents = target.info.maxRecents; info.windowLayout = target.info.windowLayout; info.resizeMode = target.info.resizeMode; - info.maxAspectRatio = target.info.maxAspectRatio; - info.minAspectRatio = target.info.minAspectRatio; + info.setMaxAspectRatio(target.info.getMaxAspectRatio()); + info.setMinAspectRatio(target.info.getManifestMinAspectRatio()); info.supportsSizeChanges = target.info.supportsSizeChanges; info.requestedVrComponent = target.info.requestedVrComponent; @@ -8157,7 +8157,7 @@ public class PackageParser { return; } - info.maxAspectRatio = maxAspectRatio; + info.setMaxAspectRatio(maxAspectRatio); mHasMaxAspectRatio = true; } @@ -8173,7 +8173,7 @@ public class PackageParser { return; } - info.minAspectRatio = minAspectRatio; + info.setMinAspectRatio(minAspectRatio); mHasMinAspectRatio = true; } diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index b660a00443a4..fdd2c2ab83e3 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -474,9 +474,9 @@ public class PackageInfoWithoutStateUtils { ai.screenOrientation = a.getScreenOrientation(); ai.resizeMode = a.getResizeMode(); Float maxAspectRatio = a.getMaxAspectRatio(); - ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f; + ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f); Float minAspectRatio = a.getMinAspectRatio(); - ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f; + ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f); ai.supportsSizeChanges = a.getSupportsSizeChanges(); ai.requestedVrComponent = a.getRequestedVrComponent(); ai.rotationAnimation = a.getRotationAnimation(); diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index d89c3d591d46..df4ade09753b 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -21,7 +21,6 @@ import android.util.ArrayMap; import android.util.Slog; import java.io.PrintWriter; -import java.util.function.BiConsumer; import java.util.function.Consumer; /** @@ -355,23 +354,6 @@ public class RemoteCallbackList<E extends IInterface> { } /** - * Performs {@code action} on each callback and associated cookie, calling {@link - * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping. - * - * @hide - */ - public <C> void broadcast(BiConsumer<E, C> action) { - int itemCount = beginBroadcast(); - try { - for (int i = 0; i < itemCount; i++) { - action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i)); - } - } finally { - finishBroadcast(); - } - } - - /** * Returns the number of registered callbacks. Note that the number of registered * callbacks may differ from the value returned by {@link #beginBroadcast()} since * the former returns the number of callbacks registered at the time of the call diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e9a79e70fd74..d47ae2783336 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1261,6 +1261,8 @@ public class PhoneStateListener { // default implementation empty } + + /** * The callback methods need to be called on the handler thread where * this object was created. If the binder did that for us it'd be nice. @@ -1579,6 +1581,11 @@ public class PhoneStateListener { public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) { // default implementation empty } + + public void onLinkCapacityEstimateChanged( + List<LinkCapacityEstimate> linkCapacityEstimateList) { + // default implementation empty + } } private void log(String s) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index e3d3dec60151..a0875a275ec6 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -565,6 +565,21 @@ public class TelephonyCallback { @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; + + /** + * Event for changes to the link capacity estimate (LCE) + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * + * @see LinkCapacityEstimateChangedListener#onLinkCapacityEstimateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_LINK_CAPACITY_ESTIMATE_CHANGED = 37; + + /** * @hide */ @@ -604,7 +619,8 @@ public class TelephonyCallback { EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, EVENT_DATA_ENABLED_CHANGED, EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED, - EVENT_LEGACY_CALL_STATE_CHANGED + EVENT_LEGACY_CALL_STATE_CHANGED, + EVENT_LINK_CAPACITY_ESTIMATE_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface TelephonyEvent { @@ -1370,6 +1386,25 @@ public class TelephonyCallback { @TelephonyManager.DataEnabledReason int reason); } + /** + * Interface for link capacity estimate changed listener. + * + * @hide + */ + @SystemApi + public interface LinkCapacityEstimateChangedListener { + /** + * Callback invoked when the link capacity estimate (LCE) changes + * + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + * The list size is at least 1. + * In case of a dual connected network, the list size could be 2. + * Use {@link LinkCapacityEstimate#getType()} to get the type of each element. + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + void onLinkCapacityEstimateChanged( + @NonNull List<LinkCapacityEstimate> linkCapacityEstimateList); + } /** * The callback methods need to be called on the handler thread where @@ -1718,5 +1753,16 @@ public class TelephonyCallback { () -> listener.onAllowedNetworkTypesChanged(reason, allowedNetworkType))); } + + public void onLinkCapacityEstimateChanged( + List<LinkCapacityEstimate> linkCapacityEstimateList) { + LinkCapacityEstimateChangedListener listener = + (LinkCapacityEstimateChangedListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onLinkCapacityEstimateChanged( + linkCapacityEstimateList))); + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3fa63d8c1a9c..1ec12fe12b36 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -842,9 +842,23 @@ public class TelephonyRegistryManager { } } + /** + * Notify that the link capacity estimate has changed. + * @param slotIndex for the phone object that gets the updated link capacity estimate + * @param subId for subscription that gets the updated link capacity estimate + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + */ + public void notifyLinkCapacityEstimateChanged(int slotIndex, int subId, + List<LinkCapacityEstimate> linkCapacityEstimateList) { + try { + sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList); + } catch (RemoteException ex) { + // system server crash + } + } + public @NonNull Set<Integer> getEventsFromCallback( @NonNull TelephonyCallback telephonyCallback) { - Set<Integer> eventList = new ArraySet<>(); if (telephonyCallback instanceof TelephonyCallback.ServiceStateListener) { @@ -976,6 +990,10 @@ public class TelephonyRegistryManager { eventList.add(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED); } + if (telephonyCallback instanceof TelephonyCallback.LinkCapacityEstimateChangedListener) { + eventList.add(TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED); + } + return eventList; } diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl index 26eaac0a2bf5..9b3561400efc 100644 --- a/core/java/android/view/IScrollCaptureCallbacks.aidl +++ b/core/java/android/view/IScrollCaptureCallbacks.aidl @@ -27,13 +27,6 @@ import android.view.Surface; */ interface IScrollCaptureCallbacks { /** - * Provides the result of WindowManagerService#requestScrollCapture - * - * @param response the response which describes the result - */ - oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); - - /** * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed * the request and is ready to begin capturing images. */ diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl index c55e88800393..3a6b69397919 100644 --- a/core/java/android/view/IScrollCaptureConnection.aidl +++ b/core/java/android/view/IScrollCaptureConnection.aidl @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.os.ICancellationSignal; +import android.view.IScrollCaptureCallbacks; import android.view.Surface; @@ -31,11 +32,12 @@ interface IScrollCaptureConnection { /** * Informs the target that it has been selected for scroll capture. * - * @param surface a return channel for image buffers + * @param surface used to shuttle image buffers between processes + * @param callbacks a return channel for requests * - * @return a cancallation signal which is used cancel the request + * @return a cancallation signal which is used cancel the start request */ - ICancellationSignal startCapture(in Surface surface); + ICancellationSignal startCapture(in Surface surface, IScrollCaptureCallbacks callbacks); /** * Request the target capture an image within the provided rectangle. diff --git a/core/java/android/view/IScrollCaptureResponseListener.aidl b/core/java/android/view/IScrollCaptureResponseListener.aidl new file mode 100644 index 000000000000..7220f6ca5661 --- /dev/null +++ b/core/java/android/view/IScrollCaptureResponseListener.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; +import android.view.ScrollCaptureResponse; +import android.view.Surface; + +/** + * Asynchronous callback channel for the initial response to a scroll capture request. + * + * {@hide} + */ +interface IScrollCaptureResponseListener { + /** + * Provides the initial response to a scroll capture request. + * + * @param response the response which describes the result + */ + oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); +} diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index fb012ebbc3db..8d59ba0b1f76 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -26,7 +26,7 @@ import android.view.DisplayCutout; import android.view.DragEvent; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.KeyEvent; import android.view.MotionEvent; import android.window.ClientWindowFrames; @@ -134,5 +134,5 @@ oneway interface IWindow { * * @param callbacks to receive responses */ - void requestScrollCapture(in IScrollCaptureCallbacks callbacks); + void requestScrollCapture(in IScrollCaptureResponseListener callbacks); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index b345b2e58252..a42126f18357 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -42,7 +42,7 @@ import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; import android.view.ISystemGestureExclusionListener; @@ -744,10 +744,10 @@ interface IWindowManager * @param behindClient token for a window, used to filter the search to windows behind it, or * {@code null} to accept a window at any zOrder * @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids - * @param callbacks the object to receive replies + * @param listener the object to receive the response */ void requestScrollCapture(int displayId, IBinder behindClient, int taskId, - IScrollCaptureCallbacks callbacks); + IScrollCaptureResponseListener listener); /** * Holds the WM lock for the specified amount of milliseconds. diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index 3456e016c42c..a6d786e1db21 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -32,6 +32,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -50,8 +51,9 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final Object mLock = new Object(); private final Rect mScrollBounds; private final Point mPositionInWindow; - private final CloseGuard mCloseGuard; private final Executor mUiThread; + private final CloseGuard mCloseGuard = new CloseGuard(); + private ScrollCaptureCallback mLocal; private IScrollCaptureCallbacks mRemote; @@ -60,42 +62,38 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private CancellationSignal mCancellation; - private volatile boolean mStarted; - private volatile boolean mConnected; + private volatile boolean mActive; /** * Constructs a ScrollCaptureConnection. * + * @param uiThread an executor for the UI thread of the containing View * @param selectedTarget the target the client is controlling - * @param remote the callbacks to reply to system requests * * @hide */ public ScrollCaptureConnection( @NonNull Executor uiThread, - @NonNull ScrollCaptureTarget selectedTarget, - @NonNull IScrollCaptureCallbacks remote) { + @NonNull ScrollCaptureTarget selectedTarget) { mUiThread = requireNonNull(uiThread, "<uiThread> must non-null"); requireNonNull(selectedTarget, "<selectedTarget> must non-null"); - mRemote = requireNonNull(remote, "<callbacks> must non-null"); mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()), "target.getScrollBounds() must be non-null to construct a client"); - mLocal = selectedTarget.getCallback(); mPositionInWindow = new Point(selectedTarget.getPositionInWindow()); - - mCloseGuard = new CloseGuard(); - mCloseGuard.open("close"); - mConnected = true; } @BinderThread @Override - public ICancellationSignal startCapture(Surface surface) throws RemoteException { - checkConnected(); + public ICancellationSignal startCapture(@NonNull Surface surface, + @NonNull IScrollCaptureCallbacks remote) throws RemoteException { + + mCloseGuard.open("close"); + if (!surface.isValid()) { throw new RemoteException(new IllegalArgumentException("surface must be valid")); } + mRemote = requireNonNull(remote, "<callbacks> must non-null"); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -110,7 +108,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @UiThread private void onStartCaptureCompleted() { - mStarted = true; + mActive = true; try { mRemote.onCaptureStarted(); } catch (RemoteException e) { @@ -119,13 +117,11 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { } } - @BinderThread @Override public ICancellationSignal requestImage(Rect requestRect) throws RemoteException { Trace.beginSection("requestImage"); - checkConnected(); - checkStarted(); + checkActive(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -152,8 +148,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @BinderThread @Override public ICancellationSignal endCapture() throws RemoteException { - checkConnected(); - checkStarted(); + checkActive(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -167,64 +162,48 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @UiThread private void onEndCaptureCompleted() { - synchronized (mLock) { - mStarted = false; - try { + mActive = false; + try { + if (mRemote != null) { mRemote.onCaptureEnded(); - } catch (RemoteException e) { - Log.w(TAG, "Shutting down due to error: ", e); - close(); } + } catch (RemoteException e) { + Log.w(TAG, "Caught exception confirming capture end!", e); + } finally { + close(); } } @BinderThread @Override public void close() { - if (mStarted) { - Log.w(TAG, "close(): capture is still started?! Ending now."); - + if (mActive) { + if (mCancellation != null) { + Log.w(TAG, "close(): cancelling pending operation."); + mCancellation.cancel(); + mCancellation = null; + } + Log.w(TAG, "close(): capture session still active! Ending now."); // -> UiThread mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ })); - mStarted = false; + mActive = false; } - disconnect(); + mActive = false; + mSession = null; + mRemote = null; + mLocal = null; + mCloseGuard.close(); + Reference.reachabilityFence(this); } - /** - * Shuts down this client and releases references to dependent objects. No attempt is made - * to notify the controller, use with caution! - */ - private void disconnect() { - synchronized (mLock) { - mSession = null; - mConnected = false; - mStarted = false; - mRemote = null; - mLocal = null; - mCloseGuard.close(); - } - } - - public boolean isConnected() { - return mConnected; - } - - public boolean isStarted() { - return mStarted; - } - - private synchronized void checkConnected() throws RemoteException { - synchronized (mLock) { - if (!mConnected) { - throw new RemoteException(new IllegalStateException("Not connected")); - } - } + @VisibleForTesting + public boolean isActive() { + return mActive; } - private void checkStarted() throws RemoteException { + private void checkActive() throws RemoteException { synchronized (mLock) { - if (!mStarted) { + if (!mActive) { throw new RemoteException(new IllegalStateException("Not started!")); } } @@ -233,24 +212,16 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { /** @return a string representation of the state of this client */ public String toString() { return "ScrollCaptureConnection{" - + "connected=" + mConnected - + ", started=" + mStarted + + "active=" + mActive + ", session=" + mSession + ", remote=" + mRemote + ", local=" + mLocal + "}"; } - @VisibleForTesting - public CancellationSignal getCancellation() { - return mCancellation; - } - protected void finalize() throws Throwable { try { - if (mCloseGuard != null) { - mCloseGuard.warnIfOpen(); - } + mCloseGuard.warnIfOpen(); close(); } finally { super.finalize(); diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java index 564113edb3c7..8808827b248a 100644 --- a/core/java/android/view/ScrollCaptureResponse.java +++ b/core/java/android/view/ScrollCaptureResponse.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.os.Parcelable; +import android.os.RemoteException; import com.android.internal.util.DataClass; @@ -57,11 +58,22 @@ public class ScrollCaptureResponse implements Parcelable { @DataClass.PluralOf("message") private ArrayList<String> mMessages = new ArrayList<>(); - /** Whether a connection has been returned. */ + /** Whether an active connection is present. */ public boolean isConnected() { - return mConnection != null; + return mConnection != null && mConnection.asBinder().isBinderAlive(); } + /** Closes a connection returned with this response. */ + public void close() { + if (mConnection != null) { + try { + mConnection.close(); + } catch (RemoteException e) { + // Ignore + } + mConnection = null; + } + } @@ -367,10 +379,10 @@ public class ScrollCaptureResponse implements Parcelable { } @DataClass.Generated( - time = 1612282689462L, + time = 1614833185795L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") + inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 03dd10050724..0167147a1067 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -200,7 +200,8 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSyncInputWindows(long transactionObj); private static native boolean nativeGetDisplayBrightnessSupport(IBinder displayToken); private static native boolean nativeSetDisplayBrightness(IBinder displayToken, - float brightness); + float sdrBrightness, float sdrBrightnessNits, float displayBrightness, + float displayBrightnessNits); private static native long nativeReadTransactionFromParcel(Parcel in); private static native void nativeWriteTransactionToParcel(long nativeObject, Parcel out); private static native void nativeSetShadowRadius(long transactionObj, long nativeObject, @@ -2405,13 +2406,50 @@ public final class SurfaceControl implements Parcelable { * @hide */ public static boolean setDisplayBrightness(IBinder displayToken, float brightness) { + return setDisplayBrightness(displayToken, brightness, -1, brightness, -1); + } + + /** + * Sets the brightness of a display. + * + * @param displayToken + * The token for the display whose brightness is set. + * @param sdrBrightness + * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or -1.0f to + * turn the backlight off. Specifies the desired brightness of SDR content. + * @param sdrBrightnessNits + * The value of sdrBrightness converted to calibrated nits. -1 if this isn't available. + * @param displayBrightness + * A number between 0.0f (minimum brightness) and 1.0f (maximum brightness), or + * -1.0f to turn the backlight off. Specifies the desired brightness of the display itself, + * used directly for HDR content. + * @param displayBrightnessNits + * The value of displayBrightness converted to calibrated nits. -1 if this isn't + * available. + * + * @return Whether the method succeeded or not. + * + * @throws IllegalArgumentException if: + * - displayToken is null; + * - brightness is NaN or greater than 1.0f. + * + * @hide + */ + public static boolean setDisplayBrightness(IBinder displayToken, float sdrBrightness, + float sdrBrightnessNits, float displayBrightness, float displayBrightnessNits) { Objects.requireNonNull(displayToken); - if (Float.isNaN(brightness) || brightness > 1.0f - || (brightness < 0.0f && brightness != -1.0f)) { - throw new IllegalArgumentException("brightness must be a number between 0.0f and 1.0f," - + " or -1 to turn the backlight off: " + brightness); - } - return nativeSetDisplayBrightness(displayToken, brightness); + if (Float.isNaN(displayBrightness) || displayBrightness > 1.0f + || (displayBrightness < 0.0f && displayBrightness != -1.0f)) { + throw new IllegalArgumentException("displayBrightness must be a number between 0.0f " + + " and 1.0f, or -1 to turn the backlight off: " + displayBrightness); + } + if (Float.isNaN(sdrBrightness) || sdrBrightness > 1.0f + || (sdrBrightness < 0.0f && sdrBrightness != -1.0f)) { + throw new IllegalArgumentException("sdrBrightness must be a number between 0.0f " + + "and 1.0f, or -1 to turn the backlight off: " + displayBrightness); + } + return nativeSetDisplayBrightness(displayToken, sdrBrightness, sdrBrightnessNits, + displayBrightness, displayBrightnessNits); } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 9688c677b900..3ffe0c660f9a 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1390,14 +1390,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall viewRoot.mergeWithNextTransaction(mRtTransaction, frameNumber); return; } - - // Otherwise if the if the ViewRoot is not null, use deferred transaction instead. - if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid() - && mSurfaceControl != null) { - mRtTransaction.deferTransactionUntil(mSurfaceControl, - viewRoot.getSurfaceControl(), frameNumber); - } - mRtTransaction.apply(); } private Rect mRTLastReportedPosition = new Rect(); @@ -1470,12 +1462,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall */ synchronized (mSurfaceControlLock) { final ViewRootImpl viewRoot = getViewRootImpl(); - boolean deferTransaction = frameNumber > 0 && viewRoot != null - && viewRoot.mSurface.isValid() && !useBLASTSync(viewRoot); - if (deferTransaction) { - mRtTransaction.deferTransactionUntil(mSurfaceControl, - viewRoot.getSurfaceControl(), frameNumber); - } mRtTransaction.hide(mSurfaceControl); if (mRtReleaseSurfaces) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 35726c0c1f04..d462f5844a70 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5356,7 +5356,7 @@ public final class ViewRootImpl implements ViewParent, updateLocationInParentDisplay(msg.arg1, msg.arg2); } break; case MSG_REQUEST_SCROLL_CAPTURE: - handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj); + handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj); break; } } @@ -9267,10 +9267,10 @@ public final class ViewRootImpl implements ViewParent, /** * Dispatches a scroll capture request to the view hierarchy on the ui thread. * - * @param callbacks for replies + * @param listener for the response */ - public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { - mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget(); + public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) { + mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget(); } /** @@ -9317,10 +9317,10 @@ public final class ViewRootImpl implements ViewParent, * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)} * will follow. * - * @param callbacks to receive responses + * @param listener to receive responses * @see ScrollCaptureTargetSelector */ - public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { + public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) { ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mContext.getMainExecutor()); @@ -9335,7 +9335,7 @@ public final class ViewRootImpl implements ViewParent, getChildVisibleRect(rootView, rect, point); rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget); } - Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results); + Runnable onComplete = () -> dispatchScrollCaptureSearchResponse(listener, results); results.setOnCompleteListener(onComplete); if (!results.isComplete()) { mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout()); @@ -9343,8 +9343,8 @@ public final class ViewRootImpl implements ViewParent, } /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ - private void dispatchScrollCaptureSearchResult( - @NonNull IScrollCaptureCallbacks callbacks, + private void dispatchScrollCaptureSearchResponse( + @NonNull IScrollCaptureResponseListener listener, @NonNull ScrollCaptureSearchResults results) { ScrollCaptureTarget selectedTarget = results.getTopResult(); @@ -9361,7 +9361,7 @@ public final class ViewRootImpl implements ViewParent, if (selectedTarget == null) { response.setDescription("No scrollable targets found in window"); try { - callbacks.onScrollCaptureResponse(response.build()); + listener.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { Log.e(TAG, "Failed to send scroll capture search result", e); } @@ -9387,11 +9387,11 @@ public final class ViewRootImpl implements ViewParent, // Create a connection and return it to the caller ScrollCaptureConnection connection = new ScrollCaptureConnection( - mView.getContext().getMainExecutor(), selectedTarget, callbacks); + mView.getContext().getMainExecutor(), selectedTarget); response.setConnection(connection); try { - callbacks.onScrollCaptureResponse(response.build()); + listener.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { Log.w(TAG, "Failed to send scroll capture search response.", e); @@ -9691,10 +9691,10 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchScrollCaptureRequest(callbacks); + viewAncestor.dispatchScrollCaptureRequest(listener); } } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index cf5ec8de0362..c814e5add1b7 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2619,10 +2619,10 @@ public abstract class Window { /** * System request to begin scroll capture. * - * @param callbacks to receive responses + * @param listener to receive the response * @hide */ - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { } /** diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index d347f31eb934..7f6c4b474d3a 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -17,7 +17,6 @@ package android.view.translation; import android.os.IBinder; -import android.os.IRemoteCallback; import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; @@ -41,7 +40,4 @@ oneway interface ITranslationManager { void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec, in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId, int userId); - - void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId); - void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId); } diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java index 9fba95f29bdc..7c73e701b7c8 100644 --- a/core/java/android/view/translation/UiTranslationManager.java +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -16,36 +16,28 @@ package android.view.translation; -import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.assist.ActivityId; import android.content.Context; -import android.os.Binder; -import android.os.Bundle; -import android.os.IRemoteCallback; import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; import android.view.View; import android.view.autofill.AutofillId; -import com.android.internal.annotations.GuardedBy; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.concurrent.Executor; -// TODO(b/178044703): Describe what UI Translation is. /** * The {@link UiTranslationManager} class provides ways for apps to use the ui translation * function in framework. + * + * @hide */ +@SystemApi public final class UiTranslationManager { private static final String TAG = "UiTranslationManager"; @@ -96,14 +88,6 @@ public final class UiTranslationManager { public @interface UiTranslationState { } - // Keys for the data transmitted in the internal UI Translation state callback. - /** @hide */ - public static final String EXTRA_STATE = "state"; - /** @hide */ - public static final String EXTRA_SOURCE_LOCALE = "source_locale"; - /** @hide */ - public static final String EXTRA_TARGET_LOCALE = "target_locale"; - @NonNull private final Context mContext; @@ -127,12 +111,9 @@ public final class UiTranslationManager { * @param destSpec {@link TranslationSpec} for the translated data. * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated * @param taskId the Activity Task id which needs ui translation - * - * @hide */ // TODO, hide the APIs @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void startTranslation(@NonNull TranslationSpec sourceSpec, @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds, int taskId) { @@ -160,11 +141,8 @@ public final class UiTranslationManager { * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list * @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} - * - * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void startTranslation(@NonNull TranslationSpec sourceSpec, @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds, @NonNull ActivityId activityId) { @@ -193,12 +171,9 @@ public final class UiTranslationManager { * NOTE: Please use {@code finishTranslation(ActivityId)} instead. * * @param taskId the Activity Task id which needs ui translation - * - * @hide */ // TODO, hide the APIs @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void finishTranslation(int taskId) { try { mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED, @@ -216,11 +191,8 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} - * - * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void finishTranslation(@NonNull ActivityId activityId) { try { Objects.requireNonNull(activityId); @@ -240,12 +212,9 @@ public final class UiTranslationManager { * NOTE: Please use {@code pauseTranslation(ActivityId)} instead. * * @param taskId the Activity Task id which needs ui translation - * - * @hide */ // TODO, hide the APIs @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void pauseTranslation(int taskId) { try { mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED, @@ -263,11 +232,8 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} - * - * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void pauseTranslation(@NonNull ActivityId activityId) { try { Objects.requireNonNull(activityId); @@ -287,12 +253,9 @@ public final class UiTranslationManager { * NOTE: Please use {@code resumeTranslation(ActivityId)} instead. * * @param taskId the Activity Task id which needs ui translation - * - * @hide */ // TODO, hide the APIs @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void resumeTranslation(int taskId) { try { mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED, @@ -310,11 +273,8 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} - * - * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) - @SystemApi public void resumeTranslation(@NonNull ActivityId activityId) { try { Objects.requireNonNull(activityId); @@ -326,104 +286,4 @@ public final class UiTranslationManager { throw e.rethrowFromSystemServer(); } } - - // TODO(b/178044703): Fix the View API link when it becomes public. - /** - * Register for notifications of UI Translation state changes on the foreground activity. This - * is available to the owning application itself and also the current input method. - * <p> - * The application whose UI is being translated can use this to customize the UI Translation - * behavior in ways that aren't made easy by methods like - * View#onCreateTranslationRequest(). - * <p> - * Input methods can use this to offer complementary features to UI Translation; for example, - * enabling outgoing message translation when the system is translating incoming messages in a - * communication app. - * - * @param callback the callback to register for receiving the state change - * notifications - */ - public void registerUiTranslationStateCallback( - @NonNull @CallbackExecutor Executor executor, - @NonNull UiTranslationStateCallback callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - synchronized (mCallbacks) { - if (mCallbacks.containsKey(callback)) { - Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;" - + " ignoring."); - return; - } - final IRemoteCallback remoteCallback = - new UiTranslationStateRemoteCallback(executor, callback); - try { - mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - mCallbacks.put(callback, remoteCallback); - } - } - - /** - * Unregister {@code callback}. - * - * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback) - */ - public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) { - Objects.requireNonNull(callback); - - synchronized (mCallbacks) { - final IRemoteCallback remoteCallback = mCallbacks.get(callback); - if (remoteCallback == null) { - Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring."); - return; - } - try { - mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - mCallbacks.remove(callback); - } - } - - @NonNull - @GuardedBy("mCallbacks") - private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>(); - - private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub { - private final Executor mExecutor; - private final UiTranslationStateCallback mCallback; - - UiTranslationStateRemoteCallback(Executor executor, - UiTranslationStateCallback callback) { - mExecutor = executor; - mCallback = callback; - } - - @Override - public void sendResult(Bundle bundle) { - Binder.clearCallingIdentity(); - mExecutor.execute(() -> { - int state = bundle.getInt(EXTRA_STATE); - switch (state) { - case STATE_UI_TRANSLATION_STARTED: - case STATE_UI_TRANSLATION_RESUMED: - mCallback.onStarted( - bundle.getString(EXTRA_SOURCE_LOCALE), - bundle.getString(EXTRA_TARGET_LOCALE)); - break; - case STATE_UI_TRANSLATION_PAUSED: - mCallback.onPaused(); - break; - case STATE_UI_TRANSLATION_FINISHED: - mCallback.onFinished(); - break; - default: - Log.wtf(TAG, "Unexpected translation state:" + state); - } - }); - } - } } diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java deleted file mode 100644 index 1946b703935d..000000000000 --- a/core/java/android/view/translation/UiTranslationStateCallback.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.translation; - -import android.annotation.NonNull; - -import java.util.concurrent.Executor; - -/** - * Callback for listening to UI Translation state changes. See {@link - * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}. - */ -public interface UiTranslationStateCallback { - - /** - * The system is requesting translation of the UI from {@code sourceLocale} to {@code - * targetLocale}. - * <p> - * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has - * changed; or called again after {@link #onPaused()}. - */ - void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale); - - /** - * The system is requesting that the application temporarily show the UI contents in their - * original language. - */ - void onPaused(); - - /** - * The UI Translation session has ended. - */ - void onFinished(); -} diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index c203c7903256..05c00471ee57 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -177,7 +177,7 @@ public class EdgeEffect { private long mStartTime; private float mDuration; private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY; - private float mStretchDistanceFraction = 0.1f; + private float mStretchDistanceFraction = 1f; private float mStretchDistance = -1f; private final Interpolator mInterpolator = new DecelerateInterpolator(); @@ -656,7 +656,7 @@ public class EdgeEffect { // for now leverage placeholder logic if no stretch distance is provided to // consume the displacement ratio times the minimum of the width or height mStretchDistance > 0 ? mStretchDistance : - (mStretchDistanceFraction * Math.min(mWidth, mHeight)) + (mStretchDistanceFraction * Math.max(mWidth, mHeight)) ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java b/core/java/android/window/SizeConfigurationBuckets.aidl index a554e5f583e7..adb57f06da7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java +++ b/core/java/android/window/SizeConfigurationBuckets.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,6 @@ * limitations under the License. */ -package com.android.systemui.screenshot; +package android.window; -import java.util.function.Consumer; - -/** Accepts and retains the most recent value for verification */ -class TestableConsumer<T> implements Consumer<T> { - T mValue; - - @Override - public void accept(T t) { - mValue = t; - } - - public T getValue() { - return mValue; - } -} +parcelable SizeConfigurationBuckets; diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java new file mode 100644 index 000000000000..7422f2449a8d --- /dev/null +++ b/core/java/android/window/SizeConfigurationBuckets.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Configuration; +import android.os.Parcelable; +import android.util.SparseIntArray; + +import com.android.internal.util.DataClass; + +import java.util.Arrays; + +/** + * Contains size-configuration buckets used to prevent excessive configuration changes during + * resize. + * + * These configurations are collected from application's resources based on size-sensitive + * qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 + * and drawable-sw400dp will be added to both as 400. + * + * @hide + */ +@DataClass(genAidl = true) +public final class SizeConfigurationBuckets implements Parcelable { + + /** Horizontal (screenWidthDp) buckets */ + @Nullable + private final int[] mHorizontal; + + /** Vertical (screenHeightDp) buckets */ + @Nullable + private final int[] mVertical; + + /** Smallest (smallestScreenWidthDp) buckets */ + @Nullable + private final int[] mSmallest; + + public SizeConfigurationBuckets(Configuration[] sizeConfigurations) { + SparseIntArray horizontal = new SparseIntArray(); + SparseIntArray vertical = new SparseIntArray(); + SparseIntArray smallest = new SparseIntArray(); + for (int i = sizeConfigurations.length - 1; i >= 0; i--) { + Configuration config = sizeConfigurations[i]; + if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + vertical.put(config.screenHeightDp, 0); + } + if (config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { + horizontal.put(config.screenWidthDp, 0); + } + if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { + smallest.put(config.smallestScreenWidthDp, 0); + } + } + mHorizontal = horizontal.copyKeys(); + mVertical = vertical.copyKeys(); + mSmallest = smallest.copyKeys(); + } + + /** + * Get the changes between two configurations but don't count changes in sizes if they don't + * cross boundaries that are important to the app. + * + * This is a static helper to deal with null `buckets`. When no buckets have been specified, + * this actually filters out all 3 size-configs. This is legacy behavior. + */ + public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig, + @Nullable SizeConfigurationBuckets buckets) { + if (buckets == null) { + return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE); + } + if ((diff & CONFIG_SCREEN_SIZE) != 0) { + final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp, + newConfig.screenWidthDp) + || buckets.crossesVerticalSizeThreshold(oldConfig.screenHeightDp, + newConfig.screenHeightDp); + if (!crosses) { + diff &= ~CONFIG_SCREEN_SIZE; + } + } + if ((diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0) { + final int oldSmallest = oldConfig.smallestScreenWidthDp; + final int newSmallest = newConfig.smallestScreenWidthDp; + if (!buckets.crossesSmallestSizeThreshold(oldSmallest, newSmallest)) { + diff &= ~CONFIG_SMALLEST_SCREEN_SIZE; + } + } + return diff; + } + + private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mHorizontal, firstDp, secondDp); + } + + private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mVertical, firstDp, secondDp); + } + + private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) { + return crossesSizeThreshold(mSmallest, firstDp, secondDp); + } + + /** + * The purpose of this method is to decide whether the activity needs to be relaunched upon + * changing its size. In most cases the activities don't need to be relaunched, if the resize + * is small, all the activity content has to do is relayout itself within new bounds. There are + * cases however, where the activity's content would be completely changed in the new size and + * the full relaunch is required. + * + * The activity will report to us vertical and horizontal thresholds after which a relaunch is + * required. These thresholds are collected from the application resource qualifiers. For + * example, if application has layout-w600dp resource directory, then it needs a relaunch when + * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if + * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side + * of the threshold. + */ + private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, + int secondDp) { + if (thresholds == null) { + return false; + } + for (int i = thresholds.length - 1; i >= 0; i--) { + final int threshold = thresholds[i]; + if ((firstDp < threshold && secondDp >= threshold) + || (firstDp >= threshold && secondDp < threshold)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " " + + Arrays.toString(mSmallest); + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/SizeConfigurationBuckets.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new SizeConfigurationBuckets. + * + * @param horizontal + * Horizontal (screenWidthDp) buckets + * @param vertical + * Vertical (screenHeightDp) buckets + * @param smallest + * Smallest (smallestScreenWidthDp) buckets + */ + @DataClass.Generated.Member + public SizeConfigurationBuckets( + @Nullable int[] horizontal, + @Nullable int[] vertical, + @Nullable int[] smallest) { + this.mHorizontal = horizontal; + this.mVertical = vertical; + this.mSmallest = smallest; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Horizontal (screenWidthDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getHorizontal() { + return mHorizontal; + } + + /** + * Vertical (screenHeightDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getVertical() { + return mVertical; + } + + /** + * Smallest (smallestScreenWidthDp) buckets + */ + @DataClass.Generated.Member + public @Nullable int[] getSmallest() { + return mSmallest; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mHorizontal != null) flg |= 0x1; + if (mVertical != null) flg |= 0x2; + if (mSmallest != null) flg |= 0x4; + dest.writeByte(flg); + if (mHorizontal != null) dest.writeIntArray(mHorizontal); + if (mVertical != null) dest.writeIntArray(mVertical); + if (mSmallest != null) dest.writeIntArray(mSmallest); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ SizeConfigurationBuckets(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray(); + int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray(); + int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray(); + + this.mHorizontal = horizontal; + this.mVertical = vertical; + this.mSmallest = smallest; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<SizeConfigurationBuckets> CREATOR + = new Parcelable.Creator<SizeConfigurationBuckets>() { + @Override + public SizeConfigurationBuckets[] newArray(int size) { + return new SizeConfigurationBuckets[size]; + } + + @Override + public SizeConfigurationBuckets createFromParcel(@NonNull android.os.Parcel in) { + return new SizeConfigurationBuckets(in); + } + }; + + @DataClass.Generated( + time = 1615845864280L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java", + inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index e602cd2c8890..a60b31078a86 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -504,8 +504,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { final File visibleFile = getFileForDocId(documentId, true); final int pfdMode = ParcelFileDescriptor.parseMode(mode); - if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) { - return openFileForRead(file); + if (visibleFile == null) { + return ParcelFileDescriptor.open(file, pfdMode); + } else if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) { + return openFileForRead(visibleFile); } else { try { // When finished writing, kick off media scanner @@ -522,6 +524,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { private ParcelFileDescriptor openFileForRead(final File target) throws FileNotFoundException { final Uri uri = MediaStore.scanFile(getContext().getContentResolver(), target); + if (uri == null) { + Log.w(TAG, "Failed to retrieve media store URI for: " + target); + return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY); + } // Passing the calling uid via EXTRA_MEDIA_CAPABILITIES_UID, so that the decision to // transcode or not transcode can be made based upon the calling app's uid, and not based @@ -532,7 +538,8 @@ public abstract class FileSystemProvider extends DocumentsProvider { final AssetFileDescriptor afd = getContext().getContentResolver().openTypedAssetFileDescriptor(uri, "*/*", opts); if (afd == null) { - return null; + Log.w(TAG, "Failed to open with media_capabilities uid for URI: " + uri); + return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY); } return afd.getParcelFileDescriptor(); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 6049486b380c..39bde742e828 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -78,7 +78,7 @@ import android.view.ContextThemeWrapper; import android.view.CrossWindowBlurListeners; import android.view.Gravity; import android.view.IRotationWatcher.Stub; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEvent; @@ -3940,12 +3940,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** * System request to begin scroll capture. * - * @param callbacks to receive responses + * @param listener to receive the response * @hide */ @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { - getViewRootImpl().dispatchScrollCaptureRequest(callbacks); + public void requestScrollCapture(IScrollCaptureResponseListener listener) { + getViewRootImpl().dispatchScrollCaptureRequest(listener); } /** diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index b57b4b959334..f3d085814baa 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -21,6 +21,7 @@ import android.telephony.CallAttributes; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.LinkCapacityEstimate; import android.telephony.TelephonyDisplayInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; @@ -73,4 +74,5 @@ oneway interface IPhoneStateListener { void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); void onDataEnabledChanged(boolean enabled, int reason); void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType); + void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 83691ee64103..34187687c4e8 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -23,6 +23,7 @@ import android.telephony.BarringInfo; import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.LinkCapacityEstimate; import android.telephony.TelephonyDisplayInfo; import android.telephony.ims.ImsReasonInfo; import android.telephony.PhoneCapability; @@ -96,4 +97,6 @@ interface ITelephonyRegistry { in List<PhysicalChannelConfig> configs); void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason); void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType); + void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId, + in List<LinkCapacityEstimate> linkCapacityEstimateList); } diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java index 413be484dc32..131114cad460 100644 --- a/core/java/com/android/internal/util/TrafficStatsConstants.java +++ b/core/java/com/android/internal/util/TrafficStatsConstants.java @@ -21,24 +21,8 @@ package com.android.internal.util; * @hide */ public class TrafficStatsConstants { - // These tags are used by the network stack to do traffic for its own purposes. Traffic - // tagged with these will be counted toward the network stack and must stay inside the - // range defined by - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}. - public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01; - public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02; - public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03; public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; - - // These tags are used by the network stack to do traffic on behalf of apps. Traffic - // tagged with these will be counted toward the app on behalf of which the network - // stack is doing this traffic. These values must stay inside the range defined by - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and - // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}. - public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81; - public static final int TAG_SYSTEM_DNS = 0xFFFFFF82; } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index ab0149fce0a0..47341cd154d7 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -24,7 +24,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.IWindowSession; import android.view.InsetsSourceControl; @@ -159,9 +159,9 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { try { - callbacks.onScrollCaptureResponse( + listener.onScrollCaptureResponse( new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); } catch (RemoteException ex) { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 31cc77f74c44..65b8b988f38b 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1528,11 +1528,17 @@ static jboolean nativeGetDisplayBrightnessSupport(JNIEnv* env, jclass clazz, } static jboolean nativeSetDisplayBrightness(JNIEnv* env, jclass clazz, jobject displayTokenObject, - jfloat brightness) { + jfloat sdrBrightness, jfloat sdrBrightnessNits, + jfloat displayBrightness, jfloat displayBrightnessNits) { sp<IBinder> displayToken(ibinderForJavaObject(env, displayTokenObject)); if (displayToken == nullptr) { return JNI_FALSE; } + gui::DisplayBrightness brightness; + brightness.sdrWhitePoint = sdrBrightness; + brightness.sdrWhitePointNits = sdrBrightnessNits; + brightness.displayBrightness = displayBrightness; + brightness.displayBrightnessNits = displayBrightnessNits; status_t error = SurfaceComposerClient::setDisplayBrightness(displayToken, brightness); return error == OK ? JNI_TRUE : JNI_FALSE; } @@ -1860,7 +1866,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSyncInputWindows }, {"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z", (void*)nativeGetDisplayBrightnessSupport }, - {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;F)Z", + {"nativeSetDisplayBrightness", "(Landroid/os/IBinder;FFFF)Z", (void*)nativeSetDisplayBrightness }, {"nativeReadTransactionFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadTransactionFromParcel }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f135d678415d..521d246dc0dc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -987,6 +987,23 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:protectionLevel="signature|appop|preinstalled" /> + <!-- Allows an application to modify and delete media files on this device or any connected + storage device without user confirmation. Applications must already be granted the + {@link #READ_EXTERNAL_STORAGE} or {@link #MANAGE_EXTERNAL_STORAGE}} permissions for this + permission to take effect. + <p>Even if applications are granted this permission, if applications want to modify or + delete media files, they also must get the access by calling + {@link android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)}, + {@link android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)}, or + {@link android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)}. + <p>This permission doesn't give read or write access directly. It only prevents the user + confirmation dialog for these requests. + <p>If applications are not granted {@link #ACCESS_MEDIA_LOCATION}, the system also pops up + the user confirmation dialog for the write request. + <p>Protection level: signature|appop|preinstalled --> + <permission android:name="android.permission.MANAGE_MEDIA" + android:protectionLevel="signature|appop|preinstalled" /> + <!-- ====================================================================== --> <!-- Permissions for accessing the device location --> <!-- ====================================================================== --> @@ -5607,6 +5624,10 @@ <!-- Attribution for Gnss Time Update service. --> <attribution android:tag="GnssTimeUpdateService" android:label="@string/gnss_time_update_service"/> + <!-- Attribution for MusicRecognitionManagerService. + <p>Not for use by third-party applications.</p> --> + <attribution android:tag="MusicRecognitionManagerService" + android:label="@string/music_recognition_manager_service"/> <application android:process="system" android:persistent="true" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2c7f9a4a5ffe..054d1080f4d4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -453,6 +453,9 @@ <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]--> <string name="gnss_time_update_service">GNSS Time Update Service</string> + <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]--> + <string name="music_recognition_manager_service">Music Recognition Manager Service</string> + <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> <string name="factory_reset_warning">Your device will be erased</string> diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 6c8b94129c82..9915e3852b8d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -133,7 +133,7 @@ public class ObjectPoolTests { int ident = 57; ActivityInfo activityInfo = new ActivityInfo(); activityInfo.flags = 42; - activityInfo.maxAspectRatio = 2.4f; + activityInfo.setMaxAspectRatio(2.4f); activityInfo.launchToken = "token"; activityInfo.applicationInfo = new ApplicationInfo(); activityInfo.packageName = "packageName"; diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 6f3d7ae5eb3c..f47fa39e7dc8 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -182,7 +182,7 @@ public class TransactionParcelTests { int ident = 57; ActivityInfo activityInfo = new ActivityInfo(); activityInfo.flags = 42; - activityInfo.maxAspectRatio = 2.4f; + activityInfo.setMaxAspectRatio(2.4f); activityInfo.launchToken = "token"; activityInfo.applicationInfo = new ApplicationInfo(); activityInfo.packageName = "packageName"; diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index 516fb76eeaf7..f3a6f9e9de17 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -74,7 +74,7 @@ public class ScrollCaptureConnectionTest { mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback); mTarget.setScrollBounds(mScrollBounds); - mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); + mConnection = new ScrollCaptureConnection(Runnable::run, mTarget); } /** Test creating a client with valid info */ @@ -83,7 +83,7 @@ public class ScrollCaptureConnectionTest { ScrollCaptureTarget target = new ScrollCaptureTarget( mView, mLocalVisibleRect, mPositionInWindow, mCallback); target.setScrollBounds(new Rect(1, 2, 3, 4)); - new ScrollCaptureConnection(Runnable::run, target, mRemote); + new ScrollCaptureConnection(Runnable::run, target); } /** Test creating a client fails if arguments are not valid. */ @@ -91,20 +91,20 @@ public class ScrollCaptureConnectionTest { public void testConstruction_requiresScrollBounds() { try { mTarget.setScrollBounds(null); - new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); + new ScrollCaptureConnection(Runnable::run, mTarget); fail("An exception was expected."); } catch (RuntimeException ex) { // Ignore, expected. } } - /** @see ScrollCaptureConnection#startCapture(Surface) */ + /** @see ScrollCaptureConnection#startCapture(Surface, IScrollCaptureCallbacks) */ @Test public void testStartCapture() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); - assertTrue(mConnection.isStarted()); + assertTrue(mConnection.isActive()); verify(mRemote, times(1)).onCaptureStarted(); verifyNoMoreInteractions(mRemote); @@ -112,11 +112,11 @@ public class ScrollCaptureConnectionTest { @Test public void testStartCapture_cancellation() throws Exception { - ICancellationSignal signal = mConnection.startCapture(mSurface); + ICancellationSignal signal = mConnection.startCapture(mSurface, mRemote); signal.cancel(); mCallback.completeStartRequest(); - assertFalse(mConnection.isStarted()); + assertFalse(mConnection.isActive()); verifyNoMoreInteractions(mRemote); } @@ -124,7 +124,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#requestImage(Rect) */ @Test public void testRequestImage() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -138,7 +138,7 @@ public class ScrollCaptureConnectionTest { @Test public void testRequestImage_cancellation() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -152,7 +152,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -167,7 +167,7 @@ public class ScrollCaptureConnectionTest { /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture_cancellation() throws Exception { - mConnection.startCapture(mSurface); + mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); reset(mRemote); @@ -179,9 +179,9 @@ public class ScrollCaptureConnectionTest { } @Test - public void testClose() throws Exception { + public void testClose() { mConnection.close(); - assertFalse(mConnection.isConnected()); + assertFalse(mConnection.isActive()); verifyNoMoreInteractions(mRemote); } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 7746bc2e273a..e0d9ecfcb336 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -205,7 +205,7 @@ public class ViewRootImplTest { @Test public void requestScrollCapture_withoutContentRoot() { final CountDownLatch latch = new CountDownLatch(1); - mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() { @Override public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); @@ -237,7 +237,7 @@ public class ViewRootImplTest { final CountDownLatch latch = new CountDownLatch(1); mViewRootImpl.setScrollCaptureRequestTimeout(100); - mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() { @Override public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 89644e2320c1..0268953eab42 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - - // Privapp permission whitelist files package { @@ -187,3 +185,17 @@ prebuilt_etc { src: "com.android.car.activityresolver.xml", filename_from_src: true, } + +prebuilt_etc { + name: "allowed_privapp_com.android.car.cluster.home", + sub_dir: "permissions", + src: "com.android.car.cluster.home.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "allowed_privapp_com.android.car.messenger", + sub_dir: "permissions", + src: "com.android.car.messenger.xml", + filename_from_src: true, +} diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml new file mode 100644 index 000000000000..4c2d614df5b0 --- /dev/null +++ b/data/etc/car/com.android.car.cluster.home.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.cluster.home"> + <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml new file mode 100644 index 000000000000..16595c30c65c --- /dev/null +++ b/data/etc/car/com.android.car.messenger.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.messenger"> + <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml index 6132d53b4651..58306be90048 100644 --- a/data/etc/car/com.android.car.shell.xml +++ b/data/etc/car/com.android.car.shell.xml @@ -20,5 +20,11 @@ <privapp-permissions package="com.android.shell"> <permission name="android.permission.INSTALL_PACKAGES" /> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/> + <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/> + <permission name="android.car.permission.CAR_DIAGNOSTICS"/> + <permission name="android.car.permission.CAR_DRIVING_STATE"/> + <permission name="android.car.permission.CAR_POWER"/> + <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 4a3bd99b8f7c..115bd9b08a53 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -823,12 +823,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, - "-1159577965": { - "message": "Focus requested for input consumer=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, "-1156118957": { "message": "Updated config=%s", "level": "DEBUG", @@ -2479,12 +2473,6 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, - "690411811": { - "message": "goodToGo(): No apps to animate", - "level": "DEBUG", - "group": "WM_DEBUG_REMOTE_ANIMATIONS", - "at": "com\/android\/server\/wm\/RemoteAnimationController.java" - }, "691515534": { "message": " Commit wallpaper becoming invisible: %s", "level": "VERBOSE", @@ -2605,12 +2593,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "883475718": { - "message": "Report configuration: %s %s %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityClientController.java" - }, "892244061": { "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", "level": "INFO", @@ -2917,6 +2899,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1305412562": { + "message": "Report configuration: %s %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityClientController.java" + }, "1316533291": { "message": "State movement: %s from:%s to:%s reason:%s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java index f3bf63bb3660..b57f7af14a0e 100644 --- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java +++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java @@ -38,8 +38,7 @@ import java.util.function.Consumer; */ public final class RippleAnimationSession { private static final String TAG = "RippleAnimationSession"; - private static final int ENTER_ANIM_DURATION = 300; - private static final int SLIDE_ANIM_DURATION = 450; + private static final int ENTER_ANIM_DURATION = 450; private static final int EXIT_ANIM_DURATION = 300; private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final TimeInterpolator PATH_INTERPOLATOR = @@ -50,26 +49,21 @@ public final class RippleAnimationSession { private Runnable mOnUpdate; private long mStartTime; private boolean mForceSoftware; - private final float mWidth, mHeight; private final ValueAnimator mSparkle = ValueAnimator.ofFloat(0, 1); - private final ArraySet<Animator> mActiveAnimations = new ArraySet<>(3); RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties, - boolean forceSoftware, float width, float height) { + boolean forceSoftware) { mProperties = properties; mForceSoftware = forceSoftware; - mWidth = width; - mHeight = height; mSparkle.addUpdateListener(anim -> { final long now = AnimationUtils.currentAnimationTimeMillis(); final long elapsed = now - mStartTime - ENTER_ANIM_DURATION; - final float phase = (float) elapsed / 1000f; - mProperties.getShader().setSecondsOffset(phase); + final float phase = (float) elapsed / 30000f; + mProperties.getShader().setNoisePhase(phase); notifyUpdate(); }); mSparkle.setDuration(ENTER_ANIM_DURATION); - mSparkle.setStartDelay(ENTER_ANIM_DURATION); mSparkle.setInterpolator(LINEAR_INTERPOLATOR); mSparkle.setRepeatCount(ValueAnimator.INFINITE); } @@ -85,7 +79,6 @@ public final class RippleAnimationSession { } @NonNull RippleAnimationSession exit(Canvas canvas) { - mSparkle.end(); if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas); else exitSoftware(); return this; @@ -93,7 +86,6 @@ public final class RippleAnimationSession { private void onAnimationEnd(Animator anim) { notifyUpdate(); - mActiveAnimations.remove(anim); } @NonNull RippleAnimationSession setOnSessionEnd( @@ -123,18 +115,18 @@ public final class RippleAnimationSession { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); + mSparkle.end(); Consumer<RippleAnimationSession> onEnd = mOnSessionEnd; if (onEnd != null) onEnd.accept(RippleAnimationSession.this); } }); expand.setInterpolator(LINEAR_INTERPOLATOR); expand.start(); - mActiveAnimations.add(expand); } private long computeDelay() { final long timePassed = AnimationUtils.currentAnimationTimeMillis() - mStartTime; - return Math.max((long) SLIDE_ANIM_DURATION - timePassed, 0); + return Math.max((long) ENTER_ANIM_DURATION - timePassed, 0); } private void notifyUpdate() { @@ -157,6 +149,7 @@ public final class RippleAnimationSession { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); + mSparkle.end(); Consumer<RippleAnimationSession> onEnd = mOnSessionEnd; if (onEnd != null) onEnd.accept(RippleAnimationSession.this); } @@ -167,7 +160,6 @@ public final class RippleAnimationSession { long delay = computeDelay(); exit.setStartDelay(delay); exit.start(); - mActiveAnimations.add(exit); } private void enterHardware(RecordingCanvas canvas) { @@ -175,54 +167,27 @@ public final class RippleAnimationSession { props = getCanvasProperties(); RenderNodeAnimator expand = new RenderNodeAnimator(props.getProgress(), .5f); - RenderNodeAnimator slideX = - new RenderNodeAnimator(props.getX(), mWidth / 2); - RenderNodeAnimator slideY = - new RenderNodeAnimator(props.getY(), mHeight / 2); expand.setTarget(canvas); - slideX.setTarget(canvas); - slideY.setTarget(canvas); - startAnimation(expand, slideX, slideY); + startAnimation(expand); } - private void startAnimation(Animator expand, - Animator slideX, Animator slideY) { - expand.setDuration(SLIDE_ANIM_DURATION); - slideX.setDuration(SLIDE_ANIM_DURATION); - slideY.setDuration(SLIDE_ANIM_DURATION); - slideX.addListener(new AnimatorListener(this)); + private void startAnimation(Animator expand) { + expand.setDuration(ENTER_ANIM_DURATION); + expand.addListener(new AnimatorListener(this)); expand.setInterpolator(LINEAR_INTERPOLATOR); - slideX.setInterpolator(PATH_INTERPOLATOR); - slideY.setInterpolator(PATH_INTERPOLATOR); expand.start(); - slideX.start(); - slideY.start(); if (!mSparkle.isRunning()) { mSparkle.start(); - mActiveAnimations.add(mSparkle); } - mActiveAnimations.add(expand); - mActiveAnimations.add(slideX); - mActiveAnimations.add(slideY); } private void enterSoftware() { ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f); - ValueAnimator slideX = ValueAnimator.ofFloat( - mProperties.getX(), mWidth / 2); - ValueAnimator slideY = ValueAnimator.ofFloat( - mProperties.getY(), mHeight / 2); expand.addUpdateListener(updatedAnimation -> { notifyUpdate(); mProperties.getShader().setProgress((Float) expand.getAnimatedValue()); }); - slideX.addUpdateListener(anim -> { - float x = (float) slideX.getAnimatedValue(); - float y = (float) slideY.getAnimatedValue(); - mProperties.setOrigin(x, y); - mProperties.getShader().setOrigin(x, y); - }); - startAnimation(expand, slideX, slideY); + startAnimation(expand); } @NonNull AnimationProperties<Float, Paint> getProperties() { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index d6bbee90d73b..fb2b9b343f0a 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -842,7 +842,7 @@ public class RippleDrawable extends LayerDrawable { if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) { RippleAnimationSession.AnimationProperties<Float, Paint> properties = createAnimationProperties(x, y, w, h); - mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps, w, h) + mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps) .setOnAnimationUpdated(() -> invalidateSelf(false)) .setOnSessionEnd(session -> { mRunningAnimations.remove(session); @@ -912,14 +912,14 @@ public class RippleDrawable extends LayerDrawable { ? mState.mColor.getColorForState(getState(), Color.BLACK) : mMaskColorFilter.getColor(); shader.setColor(color); - shader.setOrigin(x, y); + shader.setOrigin(w / 2, y / 2); + shader.setTouch(x, y); shader.setResolution(w, h); - shader.setSecondsOffset(0); + shader.setNoisePhase(0); shader.setRadius(radius); shader.setProgress(.0f); properties = new RippleAnimationSession.AnimationProperties<>( - x, y, radius, p, 0f, - shader); + w / 2, h / 2, radius, p, 0f, shader); if (mMaskShader == null) { shader.setShader(null); } else { diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index 657a32c1ac46..ea9ba325a826 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -23,11 +23,12 @@ import android.graphics.Shader; final class RippleShader extends RuntimeShader { private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n" + + "uniform vec2 in_touch;\n" + "uniform float in_progress;\n" + "uniform float in_maxRadius;\n" + "uniform vec2 in_resolution;\n" + "uniform float in_hasMask;\n" - + "uniform float in_secondsOffset;\n" + + "uniform float in_noisePhase;\n" + "uniform vec4 in_color;\n" + "uniform shader in_shader;\n"; private static final String SHADER_LIB = @@ -48,7 +49,7 @@ final class RippleShader extends RuntimeShader { + " float s = 0.0;\n" + " for (float i = 0; i < 4; i += 1) {\n" + " float l = i * 0.25;\n" - + " float h = l + 0.025;\n" + + " float h = l + 0.005;\n" + " float o = abs(sin(0.1 * PI * (t + i)));\n" + " s += threshold(n + o, l, h);\n" + " }\n" @@ -79,12 +80,12 @@ final class RippleShader extends RuntimeShader { + " float fadeIn = subProgress(0., 0.175, in_progress);\n" + " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n" + " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n" - + " float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n" + + " vec2 center = mix(in_touch, in_origin, fadeIn);\n" + + " float ring = getRingMask(p, center, in_maxRadius, fadeIn);\n" + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n" - + " float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n" - + " * ring * alpha;\n" - + " float fade = min(fadeIn, 1.-fadeOutRipple);\n" - + " vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius " + + " float sparkle = sparkles(p, in_noisePhase) * ring * alpha;\n" + + " float fade = min(fadeIn, 1. - fadeOutRipple);\n" + + " vec4 circle = in_color * (softCircle(p, center, in_maxRadius " + " * fadeIn, 0.2) * fade);\n" + " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n" + " return mix(circle, vec4(sparkle), sparkle) * mask;\n" @@ -109,14 +110,18 @@ final class RippleShader extends RuntimeShader { /** * Continuous offset used as noise phase. */ - public void setSecondsOffset(float t) { - setUniform("in_secondsOffset", t); + public void setNoisePhase(float t) { + setUniform("in_noisePhase", t); } public void setOrigin(float x, float y) { setUniform("in_origin", new float[] {x, y}); } + public void setTouch(float x, float y) { + setUniform("in_touch", new float[] {x, y}); + } + public void setProgress(float progress) { setUniform("in_progress", progress); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 4bb8e9b6581f..9dabec7a13d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -32,7 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.IWindowManager; import android.view.IWindowSession; @@ -369,9 +369,9 @@ public class SystemWindows { public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {} @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + public void requestScrollCapture(IScrollCaptureResponseListener listener) { try { - callbacks.onScrollCaptureResponse( + listener.onScrollCaptureResponse( new ScrollCaptureResponse.Builder() .setDescription("Not Implemented") .build()); diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java index 4b6e6882b50a..718977ff4296 100644 --- a/location/java/android/location/CorrelationVector.java +++ b/location/java/android/location/CorrelationVector.java @@ -77,6 +77,9 @@ public final class CorrelationVector implements Parcelable { * be encoded as signed 16 bit integer where 1 is represented by 32767 and -1 is represented * by -32768. * + * <p>The values are quantized using a 16bit integer to save on the data size since the array + * contains real data and it might grow. + * */ @NonNull public int[] getMagnitude() { diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index b0736389b906..1b743679e585 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -385,8 +385,7 @@ public class ImageWriter implements AutoCloseable { * (acquired via {@link #dequeueInputImage}). In the former case, the Image * data will be moved to this ImageWriter. Note that the Image properties * (size, format, strides, etc.) must be the same as the properties of the - * images dequeued from this ImageWriter, or this method will throw an - * {@link IllegalArgumentException}. In the latter case, the application has + * images dequeued from this ImageWriter. In the latter case, the application has * filled the input image with data. This method then passes the filled * buffer to the downstream consumer. In both cases, it's up to the caller * to ensure that the Image timestamp (in nanoseconds) is correctly set, as diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl new file mode 100644 index 000000000000..ceef73cd194a --- /dev/null +++ b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl @@ -0,0 +1,10 @@ +package android.media.musicrecognition; + +/** + * Interface from {@link MusicRecognitionService} to system to pass attribution tag. + * + * @hide + */ +oneway interface IMusicRecognitionAttributionTagCallback { + void onAttributionTag(in String attributionTag); +}
\ No newline at end of file diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl index 26543ed8bf8c..c970161a115b 100644 --- a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl +++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl @@ -4,6 +4,7 @@ import android.media.AudioFormat; import android.os.ParcelFileDescriptor; import android.os.IBinder; import android.media.musicrecognition.IMusicRecognitionServiceCallback; +import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback; /** * Interface from the system to a {@link MusicRecognitionService}. @@ -15,4 +16,6 @@ oneway interface IMusicRecognitionService { in ParcelFileDescriptor fd, in AudioFormat audioFormat, in IMusicRecognitionServiceCallback callback); + + void getAttributionTag(in IMusicRecognitionAttributionTagCallback callback); }
\ No newline at end of file diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl index 15215c4e15f1..10a65545cc7e 100644 --- a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl +++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl @@ -4,7 +4,7 @@ import android.os.Bundle; import android.media.MediaMetadata; /** - * Interface from a {@MusicRecognitionService} the system. + * Interface from a {@MusicRecognitionService} to the system. * * @hide */ diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java index 04b4c39bf0fa..385aff01d45a 100644 --- a/media/java/android/media/musicrecognition/MusicRecognitionService.java +++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java @@ -90,7 +90,7 @@ public abstract class MusicRecognitionService extends Service { try { callback.onRecognitionSucceeded(result, extras); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } @@ -99,11 +99,18 @@ public abstract class MusicRecognitionService extends Service { try { callback.onRecognitionFailed(failureCode); } catch (RemoteException e) { - e.rethrowFromSystemServer(); + throw e.rethrowFromSystemServer(); } } })); } + + @Override + public void getAttributionTag( + IMusicRecognitionAttributionTagCallback callback) throws RemoteException { + String tag = MusicRecognitionService.this.getAttributionTag(); + callback.onAttributionTag(tag); + } }; @Override diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 1870a939f0dc..7f5dd5d15dbe 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -2260,6 +2260,8 @@ static void android_media_MediaCodec_native_queueHardwareBuffer( c2_status_t c2err = sGrallocAlloc->priorGraphicAllocation(handle, &alloc); if (c2err != C2_OK) { ALOGW("Failed to wrap AHardwareBuffer into C2GraphicAllocation"); + native_handle_close(handle); + native_handle_delete(handle); throwExceptionAsNecessary(env, BAD_VALUE); return; } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 8b84bf3eb8d8..49c2b39f8904 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -43,6 +43,14 @@ static constexpr bool kPlayOnCallingThread = true; // Amount of time for a StreamManager thread to wait before closing. static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND; +// Debug flag: +// kForceLockStreamManagerStop is set to true to force lock the StreamManager +// worker thread during stop. This limits concurrency of Stream processing. +// Normally we lock the StreamManager worker thread during stop ONLY +// for SoundPools configured with a single Stream. +// +static constexpr bool kForceLockStreamManagerStop = false; + //////////// StreamMap::StreamMap(int32_t streams) { @@ -103,6 +111,7 @@ StreamManager::StreamManager( : StreamMap(streams) , mAttributes(*attributes) , mOpPackageName(std::move(opPackageName)) + , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop) { ALOGV("%s(%d, %zu, ...)", __func__, streams, threads); forEach([this](Stream *stream) { @@ -113,7 +122,8 @@ StreamManager::StreamManager( }); mThreadPool = std::make_unique<ThreadPool>( - std::min(threads, (size_t)std::thread::hardware_concurrency()), + std::min((size_t)streams, // do not make more threads than streams to play + std::min(threads, (size_t)std::thread::hardware_concurrency())), "SoundPool_"); } @@ -375,12 +385,12 @@ void StreamManager::run(int32_t id) } mRestartStreams.erase(it); mProcessingStreams.emplace(stream); - lock.unlock(); + if (!mLockStreamManagerStop) lock.unlock(); stream->stop(); ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID()); if (Stream* nextStream = stream->playPairStream()) { ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID()); - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); if (nextStream->getStopTimeNs() > 0) { // the next stream was stopped before we can move it to the active queue. ALOGV("%s(%d) stopping started streamID:%d", @@ -390,7 +400,7 @@ void StreamManager::run(int32_t id) addToActiveQueue_l(nextStream); } } else { - lock.lock(); + if (!mLockStreamManagerStop) lock.lock(); mAvailableStreams.insert(stream); } mProcessingStreams.erase(stream); diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 81ac69eb4358..85b468cd2cbc 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -437,6 +437,14 @@ private: void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock); const audio_attributes_t mAttributes; + const std::string mOpPackageName; + + // For legacy compatibility, we lock the stream manager on stop when + // there is only one stream. This allows a play to be called immediately + // after stopping, otherwise it is possible that the play might be discarded + // (returns 0) because that stream may be in the worker thread call to stop. + const bool mLockStreamManagerStop; + std::unique_ptr<ThreadPool> mThreadPool; // locked internally // mStreamManagerLock is used to lock access for transitions between the @@ -477,8 +485,6 @@ private: // The paired stream may be active or restarting. // No particular order. std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock); - - const std::string mOpPackageName; }; } // namespace android::soundpool diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 6fab9e4641b6..5afe0b5d64bc 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -50,7 +50,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.TrafficStatsConstants; +import com.android.net.module.util.NetworkStackConstants; import java.io.IOException; import java.lang.reflect.Field; @@ -239,7 +239,7 @@ public class CaptivePortalLoginActivity extends Activity { HttpURLConnection urlConnection = null; int httpResponseCode = 500; int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection( new URL(mCm.getCaptivePortalServerUrl())); diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 4dd012a8d074..44748e9cc692 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -29,7 +29,7 @@ <string name="profile_name_watch">watch</string> <!-- Title of the device association confirmation dialog. --> - <string name="confirmation_title">Set <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string> + <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string> <!-- Text of the device profile permissions explanation in the association dialog. --> <string name="profile_summary">This app is needed to manage your <xliff:g id="profile_name" example="watch">%1$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%2$s</xliff:g></string> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index f1a98adc0ab2..91a6749ac42d 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -128,9 +128,11 @@ public class CompanionDeviceActivity extends Activity { if (useDeviceProfile) { profileSummary.setVisibility(View.VISIBLE); + String deviceRef = getRequest().isSingleDevice() + ? getService().mDevicesFound.get(0).getDisplayName() + : profileName; profileSummary.setText(getString(R.string.profile_summary, - getCallingAppName(), - profileName, + deviceRef, profilePrivacyDisclaimer)); } else { profileSummary.setVisibility(View.GONE); diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index a04571496cce..b9bcddba5d0a 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -8,7 +8,7 @@ package android.net { public class ConnectivityManager { method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot(); method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); - method @NonNull public static String getPrivateDnsMode(@NonNull android.content.ContentResolver); + method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 66c45ed6fcd7..6d6a5547930c 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -5140,7 +5140,8 @@ public class ConnectivityManager { /** * Get private DNS mode from settings. * - * @param cr The ContentResolver to query private DNS mode from settings. + * @param context The Context to get its ContentResolver to query the private DNS mode from + * settings. * @return A string of private DNS mode as one of the PRIVATE_DNS_MODE_* constants. * * @hide @@ -5148,7 +5149,8 @@ public class ConnectivityManager { @SystemApi(client = MODULE_LIBRARIES) @NonNull @PrivateDnsMode - public static String getPrivateDnsMode(@NonNull ContentResolver cr) { + public static String getPrivateDnsMode(@NonNull Context context) { + final ContentResolver cr = context.getContentResolver(); String mode = Settings.Global.getString(cr, PRIVATE_DNS_MODE); if (TextUtils.isEmpty(mode)) mode = Settings.Global.getString(cr, PRIVATE_DNS_DEFAULT_MODE); // If both PRIVATE_DNS_MODE and PRIVATE_DNS_DEFAULT_MODE are not set, choose diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index f2a0e1cb83bd..68ce7d963242 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -17,8 +17,8 @@ android_library { // TODO(b/149540986): revert this change. static_libs: [ - // All other dependent components should be put in - // "SettingsLibDependenciesWithoutWifiTracker". + // All other dependent components should be put in + // "SettingsLibDependenciesWithoutWifiTracker". "WifiTrackerLib", ], @@ -27,7 +27,10 @@ android_library { resource_dirs: ["res"], - srcs: ["src/**/*.java", "src/**/*.kt"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], min_sdk_version: "21", @@ -67,6 +70,7 @@ java_defaults { "SettingsLibFooterPreference", "SettingsLibUsageProgressBarPreference", "SettingsLibCollapsingToolbarBaseActivity", + "SettingsLibTwoTargetPreference", ], } diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp new file mode 100644 index 000000000000..f2e79b9ab53b --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibTwoTargetPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.annotation_annotation", + "androidx.preference_preference", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml new file mode 100644 index 000000000000..120b0859a70a --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml index ff6a22d5523c..21fcedcc01b6 100644 --- a/packages/SettingsLib/res/layout/preference_two_target.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SettingsLib/res/layout/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml index b81dd83d2586..bd477f8068ff 100644 --- a/packages/SettingsLib/res/layout/preference_two_target_divider.xml +++ b/packages/SettingsLib/TwoTargetPreference/res/layout/preference_two_target_divider.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2017 The Android Open Source Project + Copyright (C) 2021 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml new file mode 100644 index 000000000000..32a865905267 --- /dev/null +++ b/packages/SettingsLib/TwoTargetPreference/res/values/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License + --> +<resources> + + <dimen name="two_target_pref_small_icon_size">24dp</dimen> + <dimen name="two_target_pref_medium_icon_size">32dp</dimen> +</resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java index 02895a479352..9130662021d5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java +++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,24 @@ * limitations under the License. */ -package com.android.settingslib; +package com.android.settingslib.widget; -import android.annotation.IntDef; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; +import androidx.annotation.IntDef; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +/** + * The Base preference with two target areas divided by a vertical divider + */ public class TwoTargetPreference extends Preference { @IntDef({ICON_SIZE_DEFAULT, ICON_SIZE_MEDIUM, ICON_SIZE_SMALL}) diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index ef4b97f7743f..9d5b23166190 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -32,9 +32,6 @@ <dimen name="user_spinner_padding_sides">20dp</dimen> <dimen name="user_spinner_item_height">56dp</dimen> - <dimen name="two_target_pref_small_icon_size">24dp</dimen> - <dimen name="two_target_pref_medium_icon_size">32dp</dimen> - <!-- Lock icon for preferences locked by admin --> <dimen name="restricted_icon_padding">4dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java index ad7e995412aa..fc8b5879c5fa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -27,6 +27,8 @@ import androidx.core.content.res.TypedArrayUtils; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.widget.TwoTargetPreference; + /** * Preference class that supports being disabled by a user restriction * set by a device admin. diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java index 3f0ba13ce50a..aaec909aa335 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/TwoTargetPreferenceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.settingslib; +package com.android.settingslib.widget; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_DEFAULT; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM; -import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_SMALL; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_DEFAULT; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM; +import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_SMALL; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +32,8 @@ import android.widget.LinearLayout; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 0d348e263545..1fde6c9df90a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -95,4 +95,15 @@ public interface FalsingManager { /** Call to report a ProximityEvent to the FalsingManager. */ void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent); + + /** Adds a {@link FalsingBeliefListener}. */ + void addFalsingBeliefListener(FalsingBeliefListener listener); + + /** Removes a {@link FalsingBeliefListener}. */ + void removeFalsingBeliefListener(FalsingBeliefListener listener); + + /** Listener that is alerted when falsing belief level crosses a predfined threshold. */ + interface FalsingBeliefListener { + void onFalse(); + } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 6ae759c66bea..50ffbc802a7d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -59,124 +59,127 @@ </com.android.keyguard.AlphaOptimizedRelativeLayout> <LinearLayout android:id="@+id/row1" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout android:id="@+id/row2" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout android:id="@+id/row3" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pinEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout android:id="@+id/row4" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pinEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index f709424a7d12..297c20ef8d6d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -71,121 +71,124 @@ /> </RelativeLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/simPinEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/simPinEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 2f9fed610f1d..388919e6d81c 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -72,121 +72,125 @@ /> </RelativeLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key1" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="1" /> <com.android.keyguard.NumPadKey android:id="@+id/key2" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="2" /> <com.android.keyguard.NumPadKey android:id="@+id/key3" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="3" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" + > <com.android.keyguard.NumPadKey android:id="@+id/key4" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="4" /> <com.android.keyguard.NumPadKey android:id="@+id/key5" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="5" /> <com.android.keyguard.NumPadKey android:id="@+id/key6" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="6" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" > <com.android.keyguard.NumPadKey android:id="@+id/key7" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="7" /> <com.android.keyguard.NumPadKey android:id="@+id/key8" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="8" /> <com.android.keyguard.NumPadKey android:id="@+id/key9" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" androidprv:textView="@+id/pukEntry" androidprv:digit="9" /> </LinearLayout> <LinearLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_gravity="center_horizontal" > <com.android.keyguard.NumPadButton android:id="@+id/delete_button" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" android:contentDescription="@string/keyboardview_keycode_delete" style="@style/NumPadKey.Delete" /> <com.android.keyguard.NumPadKey android:id="@+id/key0" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" + android:layout_marginEnd="@dimen/num_pad_key_margin_end" androidprv:textView="@+id/pukEntry" androidprv:digit="0" /> <com.android.keyguard.NumPadButton android:id="@+id/key_enter" - android:layout_width="0px" + android:layout_width="@dimen/num_pad_key_width" android:layout_height="match_parent" - android:layout_weight="1" style="@style/NumPadKey.Enter" android:contentDescription="@string/keyboardview_keycode_enter" /> diff --git a/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml new file mode 100644 index 000000000000..be39030451f4 --- /dev/null +++ b/packages/SystemUI/res-keyguard/values-sw360dp/dimens.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2021, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Spacing around each button used for PIN view --> + <dimen name="num_pad_key_width">84dp</dimen> + <dimen name="num_pad_row_margin_bottom">12dp</dimen> + <dimen name="num_pad_key_margin_end">20dp</dimen> +</resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 115a156b65e7..07bd2e6f0505 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -38,7 +38,7 @@ <!-- Margin around the various security views --> <dimen name="keyguard_security_view_top_margin">8dp</dimen> - <dimen name="keyguard_security_view_lateral_margin">36dp</dimen> + <dimen name="keyguard_security_view_lateral_margin">20dp</dimen> <dimen name="keyguard_eca_top_margin">18dp</dimen> @@ -87,5 +87,7 @@ <dimen name="disappear_y_translation">-32dp</dimen> <!-- Spacing around each button used for PIN view --> - <dimen name="num_pad_key_margin">2dp</dimen> + <dimen name="num_pad_key_width">72dp</dimen> + <dimen name="num_pad_row_margin_bottom">6dp</dimen> + <dimen name="num_pad_key_margin_end">12dp</dimen> </resources> diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml new file mode 100644 index 000000000000..2b0ab6f9a8d2 --- /dev/null +++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml @@ -0,0 +1,25 @@ +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid + android:color="?android:attr/colorBackground"/> + + <size + android:width="64dp" + android:height="64dp"/> +</shape> diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml index b969a1527c09..4798b437298a 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml @@ -38,7 +38,6 @@ android:id="@+id/device_management_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/monitoring_title_device_owned" style="@style/DeviceManagementDialogTitle" android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding" /> diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 0199ccb04be6..562040b0ad82 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -20,7 +20,13 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- TODO: add background protection --> + <!-- Background protection --> + <ImageView + android:id="@+id/udfps_keyguard_fp_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/fingerprint_bg" + android:visibility="gone"/> <!-- Fingerprint --> <ImageView diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db79e9634346..57c932eaa8e4 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1257,6 +1257,9 @@ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. The placeholder is the organization's name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_named_management_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> owns this device and may monitor network traffic</string> + <!-- Disclosure at the bottom of Quick Settings that indicates that the user's financed device belongs to the Creditor. The placeholder is the Creditor's name. [CHAR LIMIT=100] --> + <string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> @@ -1296,6 +1299,9 @@ <!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] --> <string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> + <!-- Monitoring dialog title for financed device [CHAR LIMIT=60] --> + <string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] --> <string name="monitoring_title_device_owned">Device management</string> @@ -1330,6 +1336,9 @@ <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]--> <string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> + <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]--> + <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string> + <!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]--> <string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 7eac9034f02a..ccba1d59c8d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -48,7 +48,6 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -74,8 +73,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final SecurityCallback mSecurityCallback; private final ConfigurationController mConfigurationController; - private final KeyguardViewController mKeyguardViewController; - private final FalsingManager mFalsingManager; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; @@ -97,13 +94,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } mTouchDown = MotionEvent.obtain(ev); } else if (mTouchDown != null) { - boolean tapResult = mFalsingManager.isFalseTap(true, 0.6); - if (tapResult - || ev.getActionMasked() == MotionEvent.ACTION_UP + if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { - if (tapResult) { - mKeyguardViewController.reset(true); - } mTouchDown.recycle(); mTouchDown = null; } @@ -207,9 +199,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard KeyguardStateController keyguardStateController, SecurityCallback securityCallback, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController, - KeyguardViewController keyguardViewController, - FalsingManager falsingManager) { + ConfigurationController configurationController) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -222,8 +212,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); mConfigurationController = configurationController; - mKeyguardViewController = keyguardViewController; - mFalsingManager = falsingManager; } @Override @@ -523,8 +511,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final ConfigurationController mConfigurationController; - private final KeyguardViewController mKeyguardViewController; - private final FalsingManager mFalsingManager; @Inject Factory(KeyguardSecurityContainer view, @@ -537,9 +523,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController, - KeyguardViewController keyguardViewController, - FalsingManager falsingManager) { + ConfigurationController configurationController) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -550,8 +534,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardStateController = keyguardStateController; mSecurityViewFlipperController = securityViewFlipperController; mConfigurationController = configurationController; - mKeyguardViewController = keyguardViewController; - mFalsingManager = falsingManager; } public KeyguardSecurityContainerController create( @@ -560,7 +542,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, securityCallback, mSecurityViewFlipperController, - mConfigurationController, mKeyguardViewController, mFalsingManager); + mConfigurationController); } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index 14b96913ea49..6cee4a4d4ad7 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -24,7 +24,6 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RippleDrawable; import android.view.ContextThemeWrapper; -import android.view.ViewGroup; import androidx.annotation.StyleRes; @@ -41,7 +40,6 @@ class NumPadAnimator { private GradientDrawable mBackground; private RippleDrawable mRipple; private GradientDrawable mRippleMask; - private int mMargin; private int mNormalColor; private int mHighlightColor; private int mStyle; @@ -55,8 +53,6 @@ class NumPadAnimator { reloadColors(context); - mMargin = context.getResources().getDimensionPixelSize(R.dimen.num_pad_key_margin); - // Actual values will be updated later, usually during an onLayout() call mAnimator = new AnimatorSet(); mExpandAnimator = ValueAnimator.ofFloat(0f, 1f); @@ -82,10 +78,6 @@ class NumPadAnimator { mAnimator.playSequentially(mExpandAnimator, mContractAnimator); } - void updateMargin(ViewGroup.MarginLayoutParams lp) { - lp.setMargins(mMargin, mMargin, mMargin, mMargin); - } - void onLayout(int height) { float startRadius = height / 2f; float endRadius = height / 4f; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 1635c49b5668..b76499a39ff8 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -23,7 +23,6 @@ import android.graphics.drawable.VectorDrawable; import android.util.AttributeSet; import android.view.ContextThemeWrapper; import android.view.MotionEvent; -import android.view.ViewGroup; import androidx.annotation.Nullable; @@ -51,13 +50,6 @@ public class NumPadButton extends AlphaOptimizedImageButton { } @Override - public void setLayoutParams(ViewGroup.LayoutParams params) { - if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params); - - super.setLayoutParams(params); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 96fceeeb8397..89c1a7fe21ca 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -178,13 +178,6 @@ public class NumPadKey extends ViewGroup { } @Override - public void setLayoutParams(ViewGroup.LayoutParams params) { - if (mAnimator != null) mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params); - - super.setLayoutParams(params); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 2036150d3679..60fdbab8482c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -71,8 +71,16 @@ abstract class UdfpsAnimationView extends FrameLayout { return false; } - private void updateAlpha() { - getDrawable().setAlpha(mPauseAuth ? mAlpha : 255); + protected void updateAlpha() { + getDrawable().setAlpha(calculateAlpha()); + } + + protected final int calculateAlpha() { + return mPauseAuth ? mAlpha : 255; + } + + boolean isPauseAuth() { + return mPauseAuth; } private int expansionToAlpha(float expansion) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java index b6d80ba14dc0..b7726f41e4a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -24,10 +24,16 @@ import android.annotation.NonNull; import android.graphics.PointF; import android.graphics.RectF; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.ViewController; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * Handles: * 1. registering for listeners when its view is attached and unregistering on view detached @@ -39,33 +45,50 @@ import com.android.systemui.util.ViewController; * - doze time event */ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> - extends ViewController<T> { + extends ViewController<T> implements Dumpable { @NonNull final StatusBarStateController mStatusBarStateController; @NonNull final StatusBar mStatusBar; + @NonNull final DumpManager mDumpManger; private boolean mNotificationShadeExpanded; private int mStatusBarState; protected UdfpsAnimationViewController( T view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { super(view); mStatusBarStateController = statusBarStateController; mStatusBar = statusBar; + mDumpManger = dumpManager; } + abstract @NonNull String getTag(); + @Override protected void onViewAttached() { mStatusBarStateController.addCallback(mStateListener); mStateListener.onStateChanged(mStatusBarStateController.getState()); mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.registerDumpable(getTag(), this); } @Override protected void onViewDetached() { mStatusBarStateController.removeCallback(mStateListener); mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.unregisterDumpable(getTag()); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState)); + pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded); + pw.println("shouldPauseAuth()=" + shouldPauseAuth()); + pw.println("isPauseAuth=" + mView.isPauseAuth()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java index b712c655a6e7..93d80e29aded 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -24,9 +27,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { protected UdfpsBpViewController( - UdfpsBpView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsBpView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsBpViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 94aeb73c4b42..797a4411b8c9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -50,8 +50,10 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.DelayableExecutor; import javax.inject.Inject; @@ -83,6 +85,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + @NonNull private final DumpManager mDumpManager; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -299,7 +303,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, - @NonNull StatusBar statusBar) { + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { mContext = context; mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the @@ -309,6 +315,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; + mKeyguardViewManager = statusBarKeyguardViewManager; + mDumpManager = dumpManager; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -457,7 +465,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { enrollView, mServerRequest.mEnrollHelper, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: UdfpsKeyguardView keyguardView = (UdfpsKeyguardView) @@ -466,7 +475,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsKeyguardViewController( keyguardView, mStatusBarStateController, - mStatusBar + mStatusBar, + mKeyguardViewManager, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_BP: // note: empty controller, currently shows no visual affordance @@ -475,7 +486,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsBpViewController( bpView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView) @@ -484,7 +496,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsFpmOtherViewController( authOtherView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); default: Log.d(TAG, "Animation for reason " + reason + " not supported yet"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java index 13d31cb87fdc..18f54166ad28 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java @@ -61,12 +61,14 @@ public abstract class UdfpsDrawable extends Drawable { */ protected void updateFingerprintIconBounds(@NonNull Rect bounds) { mFingerprintDrawable.setBounds(bounds); + invalidateSelf(); } @Override public void setAlpha(int alpha) { mAlpha = alpha; mFingerprintDrawable.setAlpha(mAlpha); + invalidateSelf(); } boolean isIlluminationShowing() { @@ -74,7 +76,11 @@ public abstract class UdfpsDrawable extends Drawable { } void setIlluminationShowing(boolean showing) { + if (mIlluminationShowing == showing) { + return; + } mIlluminationShowing = showing; + invalidateSelf(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index 727a40d313e0..cd5abd74c260 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -128,6 +128,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mSensorOutlinePaint.setAlpha(alpha); mBlueFill.setAlpha(alpha); mBlueStroke.setAlpha(alpha); + mMovingTargetFpIcon.setAlpha(alpha); invalidateSelf(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index 7985d95c7c61..75e8638e43df 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.util.AttributeSet; import android.widget.ImageView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.R; @@ -28,8 +29,9 @@ import com.android.systemui.R; * View corresponding with udfps_enroll_view.xml */ public class UdfpsEnrollView extends UdfpsAnimationView { - private final UdfpsEnrollDrawable mFingerprintDrawable; - private ImageView mFingerprintView; + @NonNull private final UdfpsEnrollDrawable mFingerprintDrawable; + @NonNull private ImageView mFingerprintView; + @NonNull private UdfpsProgressBar mProgressBar; public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -37,9 +39,17 @@ public class UdfpsEnrollView extends UdfpsAnimationView { } @Override + protected void updateAlpha() { + super.updateAlpha(); + mProgressBar.setAlpha(calculateAlpha()); + mProgressBar.getProgressDrawable().setAlpha(calculateAlpha()); + } + + @Override protected void onFinishInflate() { mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view); mFingerprintView.setImageDrawable(mFingerprintDrawable); + mProgressBar = findViewById(R.id.progress_bar); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index da8d712ebbdc..1ebbfbf84814 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -21,6 +21,7 @@ import android.graphics.PointF; import android.view.View; import com.android.systemui.R; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -32,17 +33,23 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp @NonNull private final UdfpsEnrollHelper mEnrollHelper; protected UdfpsEnrollViewController( - UdfpsEnrollView view, + @NonNull UdfpsEnrollView view, @NonNull UdfpsEnrollHelper enrollHelper, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); mEnrollHelper = enrollHelper; mProgressBar = mView.findViewById(R.id.progress_bar); mView.setEnrollHelper(mEnrollHelper); } @Override + @NonNull String getTag() { + return "UdfpsEnrollViewController"; + } + + @Override protected void onViewAttached() { super.onViewAttached(); if (mEnrollHelper.shouldShowProgressBar()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java index 587501bd1aa5..6e2e4baf492b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -27,9 +30,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> { protected UdfpsFpmOtherViewController( - UdfpsFpmOtherView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsFpmOtherView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsFpmOtherViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java index b0c5da09d916..12c15a0882f9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java @@ -36,14 +36,14 @@ import com.android.systemui.doze.DozeReceiver; public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver { private static final String TAG = "UdfpsAnimationKeyguard"; - private final int mLockScreenColor; private final int mAmbientDisplayColor; @NonNull private final Context mContext; - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; + private int mLockScreenColor; // AOD anti-burn-in offsets + private final int mMaxBurnInOffsetX; + private final int mMaxBurnInOffsetY; private float mInterpolatedDarkAmount; private float mBurnInOffsetX; private float mBurnInOffsetY; @@ -95,4 +95,10 @@ public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver mInterpolatedDarkAmount = eased; updateAodPositionAndColor(); } + + void setLockScreenColor(int color) { + if (mLockScreenColor == color) return; + mLockScreenColor = color; + updateAodPositionAndColor(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 6a9356034d22..a78c223bf883 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -16,13 +16,23 @@ package com.android.systemui.biometrics; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; +import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; +import com.android.settingslib.Utils; +import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarState; /** * View corresponding with udfps_keyguard_view.xml @@ -30,6 +40,14 @@ import com.android.systemui.R; public class UdfpsKeyguardView extends UdfpsAnimationView { private final UdfpsKeyguardDrawable mFingerprintDrawable; private ImageView mFingerprintView; + private int mWallpaperTexColor; + private int mStatusBarState; + + // used when highlighting fp icon: + private int mTextColorPrimary; + private ImageView mBgProtection; + + private AnimatorSet mAnimatorSet; public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -38,8 +56,15 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { @Override protected void onFinishInflate() { + super.onFinishInflate(); mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view); - mFingerprintView.setImageDrawable(mFingerprintDrawable); + mFingerprintView.setForeground(mFingerprintDrawable); + + mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg); + + mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); } @Override @@ -54,7 +79,114 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { return true; } + void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + if (!isShadeLocked()) { + mFingerprintView.setAlpha(1f); + mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor); + } + } + void onDozeAmountChanged(float linear, float eased) { mFingerprintDrawable.onDozeAmountChanged(linear, eased); + invalidate(); + } + + /** + * Animates in the bg protection circle behind the fp icon to highlight the icon. + */ + void animateHighlightFp() { + if (mBgProtection.getVisibility() == View.VISIBLE && mBgProtection.getAlpha() == 1f) { + // already fully highlighted, don't re-animate + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // set color and fade in since we weren't showing before + mFingerprintDrawable.setLockScreenColor(mTextColorPrimary); + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 0f, 1f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBgProtection.setVisibility(View.VISIBLE); + } + }); + + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f), + fpIconAnim); + mAnimatorSet.start(); + } + + private boolean isShadeLocked() { + return mStatusBarState == StatusBarState.SHADE_LOCKED; + } + + /** + * Animates out the bg protection circle behind the fp icon to unhighlight the icon. + */ + void animateUnhighlightFp(@Nullable Runnable onEndAnimation) { + if (mBgProtection.getVisibility() == View.GONE) { + // already hidden + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // fade out + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 1f, 0f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f), + fpIconAnim); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgProtection.setVisibility(View.GONE); + if (onEndAnimation != null) { + onEndAnimation.run(); + } + } + }); + mAnimatorSet.start(); + } + + boolean isAnimating() { + return mAnimatorSet != null && mAnimatorSet.isRunning(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 14bb3fee1174..08e5d562113d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -16,34 +16,62 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; /** * Class that coordinates non-HBM animations during keyguard authentication. */ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + private boolean mForceShow; protected UdfpsKeyguardViewController( - UdfpsKeyguardView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsKeyguardView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + mKeyguardViewManager = statusBarKeyguardViewManager; + } + + @Override + @NonNull String getTag() { + return "UdfpsKeyguardViewController"; } @Override protected void onViewAttached() { super.onViewAttached(); - mStatusBarStateController.addCallback(mStateListener); + final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mStatusBarStateController.addCallback(mStateListener); mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + mStateListener.onStateChanged(mStatusBarStateController.getState()); + mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); } @Override protected void onViewDetached() { super.onViewDetached(); mStatusBarStateController.removeCallback(mStateListener); + mAlternateAuthInterceptor.reset(); + mKeyguardViewManager.setAlternateAuthInterceptor(null); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.println("mForceShow=" + mForceShow); } /** @@ -56,7 +84,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mForceShow = forceShow; updatePauseAuth(); - // TODO: animate show/hide background protection + if (mForceShow) { + mView.animateHighlightFp(); + } else { + mView.animateUnhighlightFp(() -> mKeyguardViewManager.cancelPostAuthActions()); + } } /** @@ -76,6 +108,50 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onDozeAmountChanged(float linear, float eased) { mView.onDozeAmountChanged(linear, eased); + if (linear != 0) forceShow(false); + } + + @Override + public void onStateChanged(int statusBarState) { + mView.setStatusBarState(statusBarState); } }; + + private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor = + new StatusBarKeyguardViewManager.AlternateAuthInterceptor() { + @Override + public boolean showAlternativeAuthMethod() { + if (mForceShow) { + return false; + } + + forceShow(true); + return true; + } + + @Override + public boolean reset() { + if (!mForceShow) { + return false; + } + + forceShow(false); + return true; + } + + @Override + public boolean isShowingAlternativeAuth() { + return mForceShow; + } + + @Override + public boolean isAnimating() { + return mView.isAnimating(); + } + + @Override + public void dump(PrintWriter pw) { + pw.print(getTag()); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index 45cefcc564bf..292af3f069f5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; import com.android.systemui.classifier.FalsingDataProvider.SessionListener; +import com.android.systemui.classifier.HistoryTracker.BeliefListener; import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; @@ -62,6 +63,7 @@ public class BrightLineFalsingManager implements FalsingManager { private static final int RECENT_INFO_LOG_SIZE = 40; private static final int RECENT_SWIPE_LOG_SIZE = 20; private static final double TAP_CONFIDENCE_THRESHOLD = 0.7; + private static final double FALSE_BELIEF_THRESHOLD = 0.9; private final FalsingDataProvider mDataProvider; private final DockManager mDockManager; @@ -78,6 +80,7 @@ public class BrightLineFalsingManager implements FalsingManager { new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1); private final Collection<FalsingClassifier> mClassifiers; + private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final SessionListener mSessionListener = new SessionListener() { @Override @@ -91,22 +94,28 @@ public class BrightLineFalsingManager implements FalsingManager { } }; + private final BeliefListener mBeliefListener = belief -> { + if (belief > FALSE_BELIEF_THRESHOLD) { + mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse); + } + }; + private final FalsingDataProvider.GestureCompleteListener mGestureCompleteListener = new FalsingDataProvider.GestureCompleteListener() { @Override - public void onGestureComplete(long completionTimeMs) { - if (mPriorResults != null) { - mHistoryTracker.addResults(mPriorResults, completionTimeMs); - mPriorResults = null; - } else { - // Gestures that were not classified get treated as a false. - mHistoryTracker.addResults( - Collections.singleton( - FalsingClassifier.Result.falsed(.8, "unclassified")), - completionTimeMs); - } - } - }; + public void onGestureComplete(long completionTimeMs) { + if (mPriorResults != null) { + mHistoryTracker.addResults(mPriorResults, completionTimeMs); + mPriorResults = null; + } else { + // Gestures that were not classified get treated as a false. + mHistoryTracker.addResults( + Collections.singleton( + FalsingClassifier.Result.falsed(.8, "unclassified")), + completionTimeMs); + } + } + }; private Collection<FalsingClassifier.Result> mPriorResults; @@ -129,6 +138,7 @@ public class BrightLineFalsingManager implements FalsingManager { mDataProvider.addSessionListener(mSessionListener); mDataProvider.addGestureCompleteListener(mGestureCompleteListener); + mHistoryTracker.addBeliefListener(mBeliefListener); } @Override @@ -302,6 +312,16 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.add(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.remove(listener); + } + + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); @@ -341,6 +361,8 @@ public class BrightLineFalsingManager implements FalsingManager { mDataProvider.removeSessionListener(mSessionListener); mDataProvider.removeGestureCompleteListener(mGestureCompleteListener); mClassifiers.forEach(FalsingClassifier::cleanup); + mFalsingBeliefListeners.clear(); + mHistoryTracker.removeBeliefListener(mBeliefListener); } static void logDebug(String msg) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index aac27cb43376..d39f12488595 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -24,6 +24,8 @@ import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; /** * Simple Fake for testing where {@link FalsingManager} is required. @@ -38,6 +40,8 @@ public class FalsingManagerFake implements FalsingManager { private boolean mIsReportingEnabled; private boolean mIsFalseRobustTap; + private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); + @Override public void onSuccessfulUnlock() { @@ -127,4 +131,14 @@ public class FalsingManagerFake implements FalsingManager { public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { } + + @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.add(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mFalsingBeliefListeners.remove(listener); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index e9bb48c7b1a9..9c29f27a2e15 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -161,6 +161,16 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override + public void addFalsingBeliefListener(FalsingBeliefListener listener) { + mInternalFalsingManager.addFalsingBeliefListener(listener); + } + + @Override + public void removeFalsingBeliefListener(FalsingBeliefListener listener) { + mInternalFalsingManager.removeFalsingBeliefListener(listener); + } + + @Override public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { mInternalFalsingManager.onProximityEvent(proximityEvent); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java index be48ec415652..09bf04cb6d59 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java @@ -20,7 +20,9 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.util.time.SystemClock; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; @@ -50,6 +52,7 @@ public class HistoryTracker { private final SystemClock mSystemClock; DelayQueue<CombinedResult> mResults = new DelayQueue<>(); + private final List<BeliefListener> mBeliefListeners = new ArrayList<>(); @Inject HistoryTracker(SystemClock systemClock) { @@ -153,8 +156,17 @@ public class HistoryTracker { } mResults.add(new CombinedResult(uptimeMillis, finalScore)); + + mBeliefListeners.forEach(beliefListener -> beliefListener.onBeliefChanged(falseBelief())); + } + + void addBeliefListener(BeliefListener listener) { + mBeliefListeners.add(listener); } + void removeBeliefListener(BeliefListener listener) { + mBeliefListeners.remove(listener); + } /** * Represents a falsing score combing all the classifiers together. * @@ -197,4 +209,8 @@ public class HistoryTracker { return Long.compare(ourDelay, otherDelay); } } + + interface BeliefListener { + void onBeliefChanged(double belief); + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 5cc0e65b2a61..97803c1cf2fd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -71,7 +71,7 @@ public class KeyguardService extends Service { * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY */ private static boolean sEnableRemoteKeyguardAnimation = - SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, true); + SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false); private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index c3a523f82d9d..9d298221fb79 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -1562,7 +1562,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } public NavigationBarTransitions getBarTransitions() { - return mNavigationBarView.getBarTransitions(); + if (mNavigationBarView != null) { + return mNavigationBarView.getBarTransitions(); + } + return null; } public void finishBarAnimations() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 1411fa1ea21f..5e13fd345b81 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -15,6 +15,8 @@ */ package com.android.systemui.qs; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW; import android.app.AlertDialog; @@ -244,8 +246,14 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen if (organizationName == null) { return mContext.getString(R.string.quick_settings_disclosure_management); } - return mContext.getString(R.string.quick_settings_disclosure_named_management, - organizationName); + if (isFinancedDevice()) { + return mContext.getString( + R.string.quick_settings_financed_disclosure_named_management, + organizationName); + } else { + return mContext.getString(R.string.quick_settings_disclosure_named_management, + organizationName); + } } // end if(isDeviceManaged) if (hasCACertsInWorkProfile) { if (workProfileOrganizationName == null) { @@ -355,6 +363,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen .inflate(R.layout.quick_settings_footer_dialog, null, false); // device management section + TextView deviceManagementSubtitle = + dialogView.findViewById(R.id.device_management_subtitle); + deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization)); + CharSequence managementMessage = getManagementMessage(isDeviceManaged, deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice, workProfileOrganizationName); @@ -468,7 +480,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen } } - private String getSettingsButton() { + @VisibleForTesting + String getSettingsButton() { return mContext.getString(R.string.monitoring_button_view_policies); } @@ -490,8 +503,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen return null; } if (isDeviceManaged && organizationName != null) { - return mContext.getString( - R.string.monitoring_description_named_management, organizationName); + if (isFinancedDevice()) { + return mContext.getString(R.string.monitoring_financed_description_named_management, + organizationName, organizationName); + } else { + return mContext.getString( + R.string.monitoring_description_named_management, organizationName); + } } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) { return mContext.getString( R.string.monitoring_description_named_management, workProfileOrganizationName); @@ -557,14 +575,23 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen return message; } - private int getTitle(String deviceOwner) { - if (deviceOwner != null) { - return R.string.monitoring_title_device_owned; + @VisibleForTesting + CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) { + if (deviceOwnerOrganization != null && isFinancedDevice()) { + return mContext.getString(R.string.monitoring_title_financed_device, + deviceOwnerOrganization); } else { - return R.string.monitoring_title; + return mContext.getString(R.string.monitoring_title_device_owned); } } + private boolean isFinancedDevice() { + return mSecurityController.isDeviceManaged() + && mSecurityController.getDeviceOwnerType( + mSecurityController.getDeviceOwnerComponentOnAnyUser()) + == DEVICE_OWNER_TYPE_FINANCED; + } + private final Runnable mUpdateIcon = new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index bb8c36776d57..1ec175c01be7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -26,6 +26,8 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import android.util.AttributeSet; import android.util.IntArray; import android.util.Log; @@ -94,6 +96,25 @@ public class CropView extends View { } @Override + protected Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + SavedState ss = new SavedState(superState); + ss.mTopBoundary = getTopBoundary(); + ss.mBottomBoundary = getBottomBoundary(); + return ss; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + + setBoundaryTo(CropBoundary.TOP, ss.mTopBoundary); + setBoundaryTo(CropBoundary.BOTTOM, ss.mBottomBoundary); + } + + @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); float top = mTopCrop + mTopDelta; @@ -380,6 +401,44 @@ public class CropView extends View { */ void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition, int boundaryPositionPx); + } + + static class SavedState extends BaseSavedState { + float mTopBoundary; + float mBottomBoundary; + + /** + * Constructor called from {@link CropView#onSaveInstanceState()} + */ + SavedState(Parcelable superState) { + super(superState); + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + mTopBoundary = in.readFloat(); + mBottomBoundary = in.readFloat(); + } + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeFloat(mTopBoundary); + out.writeFloat(mBottomBoundary); + } + + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java index bc8adc9dad5b..4aead817fe8c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java @@ -213,6 +213,20 @@ class ImageExporter { CompressFormat format; boolean published; boolean deleted; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Result{"); + sb.append("uri=").append(uri); + sb.append(", requestId=").append(requestId); + sb.append(", fileName='").append(fileName).append('\''); + sb.append(", timestamp=").append(timestamp); + sb.append(", format=").append(format); + sb.append(", published=").append(published); + sb.append(", deleted=").append(deleted); + sb.append('}'); + return sb.toString(); + } } private static class Task { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java index 988b93c8ca59..7ee7c319799c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java @@ -21,7 +21,6 @@ import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.ParcelFileDescriptor; import androidx.concurrent.futures.CallbackToFutureAdapter; @@ -43,6 +42,16 @@ public class ImageLoader { @Nullable Uri uri; @Nullable File fileName; @Nullable Bitmap bitmap; + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Result{"); + sb.append("uri=").append(uri); + sb.append(", fileName=").append(fileName); + sb.append(", bitmap=").append(bitmap); + sb.append('}'); + return sb.toString(); + } } @Inject @@ -54,7 +63,7 @@ public class ImageLoader { * Loads an image via URI from ContentResolver. * * @param uri the identifier of the image to load - * @return a listenable future result + * @return a listenable future result containing a bitmap */ ListenableFuture<Result> load(Uri uri) { return CallbackToFutureAdapter.getFuture(completer -> { @@ -76,7 +85,7 @@ public class ImageLoader { * permissions to read this file/path. * * @param file the system file path of the image to load - * @return a listenable future result + * @return a listenable future result containing a bitmap */ ListenableFuture<Result> load(File file) { return CallbackToFutureAdapter.getFuture(completer -> { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 6743afa3ab59..07adc7bd7053 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java @@ -31,6 +31,7 @@ import com.android.internal.util.CallbackRegistry; import com.android.internal.util.CallbackRegistry.NotifierCallback; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -69,9 +70,6 @@ class ImageTileSet { private final Rect mBounds = new Rect(); private final Handler mHandler; - private OnContentChangedListener mOnContentChangedListener; - private OnBoundsChangedListener mOnBoundsChangedListener; - void addOnBoundsChangedListener(OnBoundsChangedListener listener) { if (mOnBoundsListeners == null) { mOnBoundsListeners = new CallbackRegistry<>( @@ -204,18 +202,17 @@ class ImageTileSet { return mBounds.height(); } - @AnyThread void clear() { - if (!mHandler.getLooper().isCurrentThread()) { - mHandler.post(this::clear); - return; - } if (mTiles.isEmpty()) { return; } mBounds.setEmpty(); - mTiles.forEach(ImageTile::close); - mTiles.clear(); + Iterator<ImageTile> i = mTiles.iterator(); + while (i.hasNext()) { + ImageTile next = i.next(); + next.close(); + i.remove(); + } notifyBoundsChanged(mBounds); notifyContentChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index db997053af23..3ac884b98136 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.HardwareRenderer; import android.graphics.RecordingCanvas; import android.graphics.Rect; @@ -30,10 +29,13 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.os.SystemClock; +import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.view.IScrollCaptureConnection; +import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import android.view.View; import android.widget.ImageView; @@ -41,14 +43,14 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot; import com.google.common.util.concurrent.ListenableFuture; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.time.ZonedDateTime; import java.util.UUID; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -61,18 +63,15 @@ import javax.inject.Inject; public class LongScreenshotActivity extends Activity { private static final String TAG = "LongScreenshotActivity"; - private static final String IMAGE_PATH_KEY = "saved-image"; - private static final String TOP_BOUNDARY_KEY = "top-boundary"; - private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary"; + public static final String EXTRA_CAPTURE_RESPONSE = "capture-response"; + private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path"; private final UiEventLogger mUiEventLogger; private final ScrollCaptureController mScrollCaptureController; - private final ScrollCaptureClient.Connection mConnection; private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; - private String mSavedImagePath; // If true, the activity is re-loading an image from storage, which should either succeed and // populate the UI or fail and finish the activity. private boolean mRestoringInstance; @@ -84,6 +83,14 @@ public class LongScreenshotActivity extends Activity { private View mShare; private CropView mCropView; private MagnifierView mMagnifierView; + private ScrollCaptureResponse mScrollCaptureResponse; + private File mSavedImagePath; + + private ListenableFuture<File> mCacheSaveFuture; + private ListenableFuture<ImageLoader.Result> mCacheLoadFuture; + + private ListenableFuture<LongScreenshot> mLongScreenshotFuture; + private LongScreenshot mLongScreenshot; private enum PendingAction { SHARE, @@ -92,35 +99,31 @@ public class LongScreenshotActivity extends Activity { } @Inject - public LongScreenshotActivity(UiEventLogger uiEventLogger, - ImageExporter imageExporter, - @Main Executor mainExecutor, - @Background Executor bgExecutor, + public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter, + @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms, Context context) { mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; mImageExporter = imageExporter; - - mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, - imageExporter); - - mConnection = ScreenshotController.takeScrollCaptureConnection(); + mScrollCaptureController = new ScrollCaptureController(context, bgExecutor, wms); } + @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")"); + super.onCreate(savedInstanceState); setContentView(R.layout.long_screenshot); - mPreview = findViewById(R.id.preview); - mSave = findViewById(R.id.save); - mCancel = findViewById(R.id.cancel); - mEdit = findViewById(R.id.edit); - mShare = findViewById(R.id.share); - mCropView = findViewById(R.id.crop_view); - mMagnifierView = findViewById(R.id.magnifier); + mPreview = requireViewById(R.id.preview); + mSave = requireViewById(R.id.save); + mCancel = requireViewById(R.id.cancel); + mEdit = requireViewById(R.id.edit); + mShare = requireViewById(R.id.share); + mCropView = requireViewById(R.id.crop_view); + mMagnifierView = requireViewById(R.id.magnifier); mCropView.setCropInteractionListener(mMagnifierView); mSave.setOnClickListener(this::onClicked); @@ -128,71 +131,188 @@ public class LongScreenshotActivity extends Activity { mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); - if (savedInstanceState != null) { - String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY); - if (!TextUtils.isEmpty(imagePath)) { - mRestoringInstance = true; - mBackgroundExecutor.execute(() -> { - Bitmap bitmap = BitmapFactory.decodeFile(imagePath); - if (bitmap == null) { - Log.e(TAG, "Failed to read bitmap from " + imagePath); - finishAndRemoveTask(); - } else { - runOnUiThread(() -> { - BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); - mPreview.setImageDrawable(drawable); - mMagnifierView.setDrawable(drawable, bitmap.getWidth(), - bitmap.getHeight()); - - mCropView.setBoundaryTo(CropView.CropBoundary.TOP, - savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f)); - mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM, - savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f)); - mRestoringInstance = false; - // Reuse the same path for subsequent restoration. - mSavedImagePath = imagePath; - Log.d(TAG, "Loaded bitmap from " + imagePath); - }); - } - }); - } - } mPreview.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateCropLocation()); + + Intent intent = getIntent(); + mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE); + + if (savedInstanceState != null) { + String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH); + if (savedImagePath == null) { + Log.e(TAG, "Missing saved state entry with key '" + KEY_SAVED_IMAGE_PATH + "'!"); + finishAndRemoveTask(); + return; + } + mSavedImagePath = new File(savedImagePath); + ImageLoader imageLoader = new ImageLoader(getContentResolver()); + mCacheLoadFuture = imageLoader.load(mSavedImagePath); + } } @Override public void onStart() { + Log.d(TAG, "onStart"); super.onStart(); - if (mPreview.getDrawable() == null && !mRestoringInstance) { - if (mConnection == null) { - Log.e(TAG, "Failed to get scroll capture connection, bailing out"); + + if (mCacheLoadFuture != null) { + Log.d(TAG, "mRestoringInstance = true"); + final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture; + mCacheLoadFuture.addListener(() -> { + Log.d(TAG, "cached bitmap load complete"); + try { + onCachedImageLoaded(future.get()); + } catch (CancellationException | ExecutionException | InterruptedException e) { + Log.e(TAG, "Failed to load cached image", e); + if (mSavedImagePath != null) { + //noinspection ResultOfMethodCallIgnored + mSavedImagePath.delete(); + mSavedImagePath = null; + } + finishAndRemoveTask(); + } + }, mUiExecutor); + mCacheLoadFuture = null; + return; + } + + if (mLongScreenshotFuture == null) { + Log.d(TAG, "mLongScreenshotFuture == null"); + // First run through, ensure we have a connection to use (see #onCreate) + if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) { + Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!"); finishAndRemoveTask(); return; } - doCapture(); + mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse); + mLongScreenshotFuture.addListener(() -> { + LongScreenshot longScreenshot; + try { + longScreenshot = mLongScreenshotFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "Error capturing long screenshot!", e); + finishAndRemoveTask(); + return; + } + if (longScreenshot.getHeight() == 0) { + Log.e(TAG, "Got a zero height result"); + finishAndRemoveTask(); + return; + } + onCaptureCompleted(longScreenshot); + }, mUiExecutor); + } else { + Log.d(TAG, "mLongScreenshotFuture != null"); } } + private void onCaptureCompleted(LongScreenshot longScreenshot) { + Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")"); + mLongScreenshot = longScreenshot; + mPreview.setImageDrawable(mLongScreenshot.getDrawable()); + updateCropLocation(); + mMagnifierView.setDrawable(mLongScreenshot.getDrawable(), + mLongScreenshot.getWidth(), mLongScreenshot.getHeight()); + // Original boundaries go from the image tile set's y=0 to y=pageSize, so + // we animate to that as a starting crop position. + float topFraction = Math.max(0, + -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight()); + float bottomFraction = Math.min(1f, + 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight()) + / (float) mLongScreenshot.getHeight()); + mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction); + mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction); + setButtonsEnabled(true); + + // Immediately export to temp image file for saved state + mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor, + mLongScreenshot.toBitmap()); + mCacheSaveFuture.addListener(() -> { + try { + // Get the temp file path to persist, used in onSavedInstanceState + mSavedImagePath = mCacheSaveFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "Error saving temp image file", e); + finishAndRemoveTask(); + } + }, mUiExecutor); + } + + private void onCachedImageLoaded(ImageLoader.Result imageResult) { + Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")"); + BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap); + mPreview.setImageDrawable(drawable); + mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(), + imageResult.bitmap.getHeight()); + mSavedImagePath = imageResult.fileName; + + setButtonsEnabled(true); + } + + private static Bitmap renderBitmap(Drawable drawable, Rect bounds) { + final RenderNode output = new RenderNode("Bitmap Export"); + output.setPosition(0, 0, bounds.width(), bounds.height()); + RecordingCanvas canvas = output.beginRecording(); + canvas.translate(-bounds.left, -bounds.top); + canvas.clipRect(bounds); + drawable.draw(canvas); + output.endRecording(); + return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); + } + @Override protected void onSaveInstanceState(Bundle outState) { + Log.d(TAG, "onSaveInstanceState"); super.onSaveInstanceState(outState); - outState.putString(IMAGE_PATH_KEY, mSavedImagePath); - outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary()); - outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary()); + if (mSavedImagePath != null) { + outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath()); + } } @Override - protected void onDestroy() { - super.onDestroy(); - if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) { + protected void onPause() { + Log.d(TAG, "onPause finishing=" + isFinishing()); + super.onPause(); + if (isFinishing()) { + if (mScrollCaptureResponse != null) { + mScrollCaptureResponse.close(); + } + cleanupCache(); + + if (mLongScreenshotFuture != null) { + mLongScreenshotFuture.cancel(true); + } + if (mLongScreenshot != null) { + mLongScreenshot.release(); + } + } + } + + void cleanupCache() { + if (mCacheSaveFuture != null) { + mCacheSaveFuture.cancel(true); + } + if (mSavedImagePath != null) { Log.d(TAG, "Deleting " + mSavedImagePath); - File file = new File(mSavedImagePath); - file.delete(); + //noinspection ResultOfMethodCallIgnored + mSavedImagePath.delete(); + mSavedImagePath = null; } } + @Override + protected void onStop() { + Log.d(TAG, "onStop"); + super.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy"); + super.onDestroy(); + } + private void setButtonsEnabled(boolean enabled) { mSave.setEnabled(enabled); mCancel.setEnabled(enabled); @@ -244,67 +364,51 @@ public class LongScreenshotActivity extends Activity { } private void startExport(PendingAction action) { + Log.d(TAG, "startExport(action = " + action + ")"); Drawable drawable = mPreview.getDrawable(); + if (drawable == null) { + Log.e(TAG, "No drawable, skipping export!"); + return; + } - Rect croppedPortion = new Rect( - 0, - (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()), - drawable.getIntrinsicWidth(), - (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary())); - ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( - mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable), - ZonedDateTime.now()); - exportFuture.addListener(() -> { - try { - ImageExporter.Result result = exportFuture.get(); - setButtonsEnabled(true); - switch (action) { - case EDIT: - doEdit(result.uri); - break; - case SHARE: - doShare(result.uri); - break; - case SAVE: - // Nothing more to do - finishAndRemoveTask(); - break; - } - } catch (InterruptedException | ExecutionException e) { - Log.e(TAG, "failed to export", e); - setButtonsEnabled(true); - } - }, mUiExecutor); - } + Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + int height = bounds.height(); + bounds.top = (int) (height * mCropView.getTopBoundary()); + bounds.bottom = (int) (height * mCropView.getBottomBoundary()); - private Bitmap getBitmap(Rect bounds, Drawable drawable) { - final RenderNode output = new RenderNode("Bitmap Export"); - output.setPosition(0, 0, bounds.width(), bounds.height()); - RecordingCanvas canvas = output.beginRecording(); - // Translating the canvas instead of setting drawable bounds since the drawable is still - // used in the preview. - canvas.translate(0, -bounds.top); - drawable.draw(canvas); - output.endRecording(); - return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); + if (bounds.isEmpty()) { + Log.w(TAG, "Crop bounds empty, skipping export."); + return; + } + + Bitmap output = renderBitmap(mPreview.getDrawable(), bounds); + ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( + mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now()); + exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor); } - private void saveCacheBitmap(ImageTileSet tileSet) { - long startTime = SystemClock.uptimeMillis(); - Bitmap bitmap = tileSet.toBitmap(); - // TODO(b/181562529) Remove this - mPreview.setImageDrawable(tileSet.getDrawable()); + private void onExportCompleted(PendingAction action, + ListenableFuture<ImageExporter.Result> exportFuture) { + setButtonsEnabled(true); + ImageExporter.Result result; try { - File file = File.createTempFile("long_screenshot", ".png", null); - FileOutputStream stream = new FileOutputStream(file); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); - stream.flush(); - stream.close(); - mSavedImagePath = file.getAbsolutePath(); - Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in " - + (SystemClock.uptimeMillis() - startTime) + "ms"); - } catch (IOException e) { - Log.e(TAG, "Failed to save bitmap", e); + result = exportFuture.get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + Log.e(TAG, "failed to export", e); + return; + } + + switch (action) { + case EDIT: + doEdit(result.uri); + break; + case SHARE: + doShare(result.uri); + break; + case SAVE: + // Nothing more to do + finishAndRemoveTask(); + break; } } @@ -313,8 +417,8 @@ public class LongScreenshotActivity extends Activity { if (drawable == null) { return; } - - float imageRatio = drawable.getBounds().width() / (float) drawable.getBounds().height(); + Rect bounds = drawable.getBounds(); + float imageRatio = bounds.width() / (float) bounds.height(); float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight(); if (imageRatio > viewRatio) { @@ -328,35 +432,4 @@ public class LongScreenshotActivity extends Activity { mCropView.setExtraPadding(0, 0); } } - - private void doCapture() { - mScrollCaptureController.start(mConnection, - new ScrollCaptureController.ScrollCaptureCallback() { - @Override - public void onError() { - Log.e(TAG, "Error capturing long screenshot!"); - finishAndRemoveTask(); - } - - @Override - public void onComplete(ImageTileSet imageTileSet, int pageSize) { - Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " - + imageTileSet.getHeight()); - mPreview.setImageDrawable(imageTileSet.getDrawable()); - updateCropLocation(); - mMagnifierView.setDrawable(imageTileSet.getDrawable(), - imageTileSet.getWidth(), imageTileSet.getHeight()); - // Original boundaries go from the image tile set's y=0 to y=pageSize, so - // we animate to that as a starting crop position. - float topFraction = Math.max(0, - -imageTileSet.getTop() / (float) imageTileSet.getHeight()); - float bottomFraction = Math.min(1f, - 1 - (imageTileSet.getBottom() - pageSize) - / (float) imageTileSet.getHeight()); - mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction); - mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction); - mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet)); - } - }); - } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 3d6dea3cd3f0..798a063f15b9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -65,6 +65,7 @@ import android.view.DisplayAddress; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; import android.view.View; import android.view.ViewTreeObserver; @@ -86,10 +87,14 @@ import com.android.systemui.screenshot.ScreenshotController.SavedImageData.Actio import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.android.systemui.util.DeviceConfigProxy; +import com.google.common.util.concurrent.ListenableFuture; + import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.function.Supplier; @@ -101,7 +106,8 @@ import javax.inject.Inject; public class ScreenshotController { private static final String TAG = logTag(ScreenshotController.class); - private static ScrollCaptureClient.Connection sScrollConnection; + private ScrollCaptureResponse mLastScrollCaptureResponse; + private ListenableFuture<ScrollCaptureResponse> mLastScrollCaptureRequest; /** * POD used in the AsyncTask which saves an image in the background. @@ -222,12 +228,6 @@ public class ScreenshotController { | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); - public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() { - ScrollCaptureClient.Connection connection = sScrollConnection; - sScrollConnection = null; - return connection; - } - @Inject ScreenshotController( Context context, @@ -352,6 +352,11 @@ public class ScreenshotController { } else { mScreenshotView.animateDismissal(); } + + if (mLastScrollCaptureResponse != null) { + mLastScrollCaptureResponse.close(); + mLastScrollCaptureResponse = null; + } } boolean isPendingSharedTransition() { @@ -526,16 +531,15 @@ public class ScreenshotController { // the reference used to locate the target window (below). withWindowAttached(() -> { mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken()); - mScrollCaptureClient.request(DEFAULT_DISPLAY, - /* onConnection */ - (connection) -> mScreenshotHandler.post(() -> - mScreenshotView.showScrollChip(() -> - /* onClick */ - runScrollCapture(connection)))); + if (mLastScrollCaptureRequest != null) { + mLastScrollCaptureRequest.cancel(true); + } + mLastScrollCaptureRequest = mScrollCaptureClient.request(DEFAULT_DISPLAY); + mLastScrollCaptureRequest.addListener(() -> + onScrollCaptureResponseReady(mLastScrollCaptureRequest), mMainExecutor); }); } - attachWindow(); mScreenshotView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @@ -560,6 +564,27 @@ public class ScreenshotController { cancelTimeout(); // restarted after animation } + private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) { + try { + if (mLastScrollCaptureResponse != null) { + mLastScrollCaptureResponse.close(); + } + mLastScrollCaptureResponse = responseFuture.get(); + final Intent intent = new Intent(mContext, LongScreenshotActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE, + mLastScrollCaptureResponse); + mScreenshotView.showScrollChip(/* onClick */ () -> { + // Clear the reference to prevent close() in dismissScreenshot + mLastScrollCaptureResponse = null; + mContext.startActivity(intent); + dismissScreenshot(false); + }); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "requestScrollCapture failed", e); + } + } + private void withWindowAttached(Runnable action) { View decorView = mWindow.getDecorView(); if (decorView.isAttachedToWindow()) { @@ -606,15 +631,6 @@ public class ScreenshotController { } } - private void runScrollCapture(ScrollCaptureClient.Connection connection) { - sScrollConnection = connection; // For LongScreenshotActivity to pick up. - - Intent intent = new Intent(mContext, LongScreenshotActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivity(intent); - dismissScreenshot(false); - } - /** * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on * failure). diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index 54b99bbe74cc..926d5c4701aa 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,18 +30,23 @@ import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; +import android.os.DeadObjectException; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Log; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; + import com.android.internal.annotations.VisibleForTesting; -import java.util.function.Consumer; +import com.google.common.util.concurrent.ListenableFuture; import javax.inject.Inject; @@ -57,61 +62,6 @@ public class ScrollCaptureClient { private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class); - - /** - * A connection to a remote window. Starts a capture session. - */ - public interface Connection { - /** - * Start a session. - - * @param sessionConsumer listener to receive the session once active - * @param maxPages the capture buffer size expressed as a multiple of the content height - */ - // TODO ListenableFuture - void start(Consumer<Session> sessionConsumer, float maxPages); - - /** - * Close the connection. Must end capture if started to avoid potential unwanted visual - * artifacts. - * - * @see Session#end(Runnable) - */ - void close(); - } - - static class CaptureResult { - public final Image image; - /** - * The area requested, in content rect space, relative to scroll-bounds. - */ - public final Rect requested; - /** - * The actual area captured, in content rect space, relative to scroll-bounds. This may be - * cropped or empty depending on available content. - */ - public final Rect captured; - - // Error? - - private CaptureResult(Image image, Rect request, Rect captured) { - this.image = image; - this.requested = request; - this.captured = captured; - } - - @Override - public String toString() { - return "CaptureResult{" - + "requested=" + requested - + " (" + requested.width() + "x" + requested.height() + ")" - + ", captured=" + captured - + " (" + captured.width() + "x" + captured.height() + ")" - + ", image=" + image - + '}'; - } - } - /** * Represents the connection to a target window and provides a mechanism for requesting tiles. */ @@ -121,10 +71,8 @@ public class ScrollCaptureClient { * and from left 0, to {@link #getPageWidth()} * * @param top the top (y) position of the tile to capture, in content rect space - * @param consumer listener to be informed of the result */ - // TODO ListenableFuture - void requestTile(int top, Consumer<CaptureResult> consumer); + ListenableFuture<CaptureResult> requestTile(int top); /** * Returns the maximum number of tiles which may be requested and retained without @@ -139,6 +87,7 @@ public class ScrollCaptureClient { */ int getTileHeight(); + /** * @return the height of scrollable content being captured */ @@ -155,11 +104,42 @@ public class ScrollCaptureClient { Rect getWindowBounds(); /** - * End the capture session, return the target app to original state. The listener - * will be called when the target app is ready to before visible and interactive. + * End the capture session, return the target app to original state. The returned Future + * will complete once the target app is ready to become visible and interactive. */ - // TODO ListenableFuture - void end(Runnable listener); + ListenableFuture<Void> end(); + + void release(); + } + + static class CaptureResult { + public final Image image; + /** + * The area requested, in content rect space, relative to scroll-bounds. + */ + public final Rect requested; + /** + * The actual area captured, in content rect space, relative to scroll-bounds. This may be + * cropped or empty depending on available content. + */ + public final Rect captured; + + CaptureResult(Image image, Rect request, Rect captured) { + this.image = image; + this.requested = request; + this.captured = captured; + } + + @Override + public String toString() { + return "CaptureResult{" + + "requested=" + requested + + " (" + requested.width() + "x" + requested.height() + ")" + + ", captured=" + captured + + " (" + captured.width() + "x" + captured.height() + ")" + + ", image=" + image + + '}'; + } } private final IWindowManager mWindowManagerService; @@ -185,10 +165,9 @@ public class ScrollCaptureClient { * Check for scroll capture support. * * @param displayId id for the display containing the target window - * @param consumer receives a connection when available */ - public void request(int displayId, Consumer<Connection> consumer) { - request(displayId, MATCH_ANY_TASK, consumer); + public ListenableFuture<ScrollCaptureResponse> request(int displayId) { + return request(displayId, MATCH_ANY_TASK); } /** @@ -196,194 +175,209 @@ public class ScrollCaptureClient { * * @param displayId id for the display containing the target window * @param taskId id for the task containing the target window or {@link #MATCH_ANY_TASK}. - * @param consumer receives a connection when available + * @return a listenable future providing the response */ - public void request(int displayId, int taskId, Consumer<Connection> consumer) { - try { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken - + ", taskId=" + taskId + ", consumer=" + consumer + ")"); + public ListenableFuture<ScrollCaptureResponse> request(int displayId, int taskId) { + return CallbackToFutureAdapter.getFuture((completer) -> { + try { + mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, + new IScrollCaptureResponseListener.Stub() { + @Override + public void onScrollCaptureResponse(ScrollCaptureResponse response) { + completer.set(response); + } + }); + + } catch (RemoteException e) { + completer.setException(e); } - mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, - new ClientCallbacks(consumer)); - } catch (RemoteException e) { - Log.e(TAG, "Ignored remote exception", e); - } + return "ScrollCaptureClient#request" + + "(displayId=" + displayId + ", taskId=" + taskId + ")"; + }); + } + + /** + * Start a scroll capture session. + * + * @param response a response provided from a request containing a connection + * @param maxPages the capture buffer size expressed as a multiple of the content height + * @return a listenable future providing the session + */ + public ListenableFuture<Session> start(ScrollCaptureResponse response, float maxPages) { + IScrollCaptureConnection connection = response.getConnection(); + return CallbackToFutureAdapter.getFuture((completer) -> { + if (connection == null || !connection.asBinder().isBinderAlive()) { + completer.setException(new DeadObjectException("No active connection!")); + return ""; + } + SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(), + response.getBoundsInWindow(), maxPages); + session.start(completer); + return "IScrollCaptureCallbacks#onCaptureStarted"; + }); } - private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements - Connection, Session, IBinder.DeathRecipient { + private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session, + IBinder.DeathRecipient { private IScrollCaptureConnection mConnection; - private Consumer<Connection> mConnectionConsumer; - private Consumer<Session> mSessionConsumer; - private Consumer<CaptureResult> mResultConsumer; - private Runnable mShutdownListener; private ImageReader mReader; - private Rect mScrollBounds; - private int mTileHeight; - private int mTileWidth; + private final int mTileHeight; + private final int mTileWidth; private Rect mRequestRect; private boolean mStarted; private ICancellationSignal mCancellationSignal; - private Rect mWindowBounds; - private Rect mBoundsInWindow; - private int mMaxTiles; + private final Rect mWindowBounds; + private final Rect mBoundsInWindow; + private final int mMaxTiles; + + private Completer<Session> mStartCompleter; + private Completer<CaptureResult> mTileRequestCompleter; + private Completer<Void> mEndCompleter; + + private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds, + Rect boundsInWindow, float maxPages) throws RemoteException { + mConnection = requireNonNull(connection); + mConnection.asBinder().linkToDeath(SessionWrapper.this, 0); + mWindowBounds = requireNonNull(windowBounds); + mBoundsInWindow = requireNonNull(boundsInWindow); + + int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); + int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); + + mTileWidth = mBoundsInWindow.width(); + mTileHeight = pxPerTile / mBoundsInWindow.width(); + mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); - private ClientCallbacks(Consumer<Connection> connectionConsumer) { - mConnectionConsumer = connectionConsumer; + if (DEBUG_SCROLL) { + Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); + Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); + } } - @BinderThread @Override - public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException { - if (DEBUG_SCROLL) { - Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")"); + public void binderDied() { + Log.d(TAG, "binderDied! The target process just crashed :-("); + // Clean up + mConnection = null; + + // Pass along the bad news. + if (mStartCompleter != null) { + mStartCompleter.setException(new DeadObjectException("The remote process died")); } - if (response.isConnected()) { - mConnection = response.getConnection(); - mConnection.asBinder().linkToDeath(this, 0); - mWindowBounds = response.getWindowBounds(); - mBoundsInWindow = response.getBoundsInWindow(); - - int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); - int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); - mTileWidth = mBoundsInWindow.width(); - mTileHeight = pxPerTile / mBoundsInWindow.width(); - if (DEBUG_SCROLL) { - Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); - Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); - Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px"); - } - mConnectionConsumer.accept(this); + if (mTileRequestCompleter != null) { + mTileRequestCompleter.setException( + new DeadObjectException("The remote process died")); + } + if (mEndCompleter != null) { + mEndCompleter.setException(new DeadObjectException("The remote process died")); } - mConnectionConsumer = null; } - @Override - public void start(Consumer<Session> sessionConsumer, float maxPages) { - if (DEBUG_SCROLL) { - Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," - + " maxPages=" + maxPages + ")"); - } - mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); + private void start(Completer<Session> completer) { mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); - mSessionConsumer = sessionConsumer; - + mStartCompleter = completer; try { - mCancellationSignal = mConnection.startCapture(mReader.getSurface()); + mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this); + completer.addCancellationListener(() -> { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + // Ignore + } + }, Runnable::run); mStarted = true; } catch (RemoteException e) { - Log.w(TAG, "Failed to start", e); mReader.close(); + completer.setException(e); } } @BinderThread @Override public void onCaptureStarted() { - if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureStarted()"); - } - mSessionConsumer.accept(this); - mSessionConsumer = null; + Log.d(TAG, "onCaptureStarted"); + mStartCompleter.set(this); } @Override - public void requestTile(int top, Consumer<CaptureResult> consumer) { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); - } - cancelPendingRequest(); + public ListenableFuture<CaptureResult> requestTile(int top) { mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); - mResultConsumer = consumer; - try { - mCancellationSignal = mConnection.requestImage(mRequestRect); - } catch (RemoteException e) { - Log.e(TAG, "Caught remote exception from requestImage", e); - } + return CallbackToFutureAdapter.getFuture((completer -> { + if (mConnection == null || !mConnection.asBinder().isBinderAlive()) { + completer.setException(new DeadObjectException("Connection is closed!")); + return ""; + } + try { + mTileRequestCompleter = completer; + mCancellationSignal = mConnection.requestImage(mRequestRect); + completer.addCancellationListener(() -> { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + // Ignore + } + }, Runnable::run); + } catch (RemoteException e) { + completer.setException(e); + } + return "IScrollCaptureCallbacks#onImageRequestCompleted"; + })); } + @BinderThread @Override public void onImageRequestCompleted(int flags, Rect contentArea) { Image image = mReader.acquireLatestImage(); - if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureBufferSent(flags=" + flags - + ", contentArea=" + contentArea + ") image=" + image); - } - // Save and clear first, since the consumer will likely request the next - // tile, otherwise the new consumer will be wiped out. - Consumer<CaptureResult> consumer = mResultConsumer; - mResultConsumer = null; - consumer.accept(new CaptureResult(image, mRequestRect, contentArea)); + mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea)); } @Override - public void end(Runnable listener) { - if (DEBUG_SCROLL) { - Log.d(TAG, "end(listener=" + listener + ")"); - } - if (mStarted) { - mShutdownListener = listener; - mReader.close(); + public ListenableFuture<Void> end() { + Log.d(TAG, "end()"); + return CallbackToFutureAdapter.getFuture(completer -> { + if (!mStarted) { + try { + mConnection.asBinder().unlinkToDeath(SessionWrapper.this, 0); + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ + } + mConnection = null; + completer.set(null); + return ""; + } + + mEndCompleter = completer; try { - // listener called from onConnectionClosed callback mConnection.endCapture(); } catch (RemoteException e) { - Log.d(TAG, "Ignored exception from endCapture()", e); - disconnect(); - listener.run(); + completer.setException(e); } - } else { - disconnect(); - listener.run(); - } + return "IScrollCaptureCallbacks#onCaptureEnded"; + }); } - @BinderThread - @Override - public void onCaptureEnded() { - close(); - if (mShutdownListener != null) { - mShutdownListener.run(); - mShutdownListener = null; - } + public void release() { + mReader.close(); } + @BinderThread @Override - public void close() { - if (mConnection != null) { - try { - mConnection.close(); - } catch (RemoteException e) { - /* ignore */ - } - disconnect(); - } - } - - // Misc - - private void disconnect() { - if (mConnection != null) { - mConnection.asBinder().unlinkToDeath(this, 0); + public void onCaptureEnded() { + try { + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ } mConnection = null; + mEndCompleter.set(null); } - /** - * The process hosting the window went away abruptly! - */ - @Override - public void binderDied() { - if (DEBUG_SCROLL) { - Log.d(TAG, "binderDied()"); - } - disconnect(); - } + // Misc @Override public int getPageHeight() { @@ -408,16 +402,5 @@ public class ScrollCaptureClient { public int getMaxTiles() { return mMaxTiles; } - - private void cancelPendingRequest() { - if (mCancellationSignal != null) { - try { - mCancellationSignal.cancel(); - } catch (RemoteException e) { - /* ignore */ - } - mCancellationSignal = null; - } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 34094cd81120..4f699041fdb3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,18 +16,27 @@ package com.android.systemui.screenshot; -import android.annotation.UiThread; import android.content.Context; -import android.net.Uri; +import android.graphics.Bitmap; +import android.graphics.HardwareRenderer; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.Drawable; import android.provider.Settings; import android.util.Log; +import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; + +import androidx.concurrent.futures.CallbackToFutureAdapter; +import androidx.concurrent.futures.CallbackToFutureAdapter.Completer; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; -import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; -import java.time.ZonedDateTime; -import java.util.UUID; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** @@ -47,44 +56,136 @@ public class ScrollCaptureController { // or if the desired bitmap size is reached. private boolean mFinishOnBoundary; - private Session mSession; - public static final int MAX_HEIGHT = 12000; private final Context mContext; - - private final Executor mUiExecutor; private final Executor mBgExecutor; - private final ImageExporter mImageExporter; private final ImageTileSet mImageTileSet; + private final ScrollCaptureClient mClient; + + private Completer<LongScreenshot> mCaptureCompleter; + + private ListenableFuture<Session> mSessionFuture; + private Session mSession; + private ListenableFuture<CaptureResult> mTileFuture; + private ListenableFuture<Void> mEndFuture; + + static class LongScreenshot { + private final ImageTileSet mImageTileSet; + private final Session mSession; + + LongScreenshot(Session session, ImageTileSet imageTileSet) { + mSession = session; + mImageTileSet = imageTileSet; + } - private ZonedDateTime mCaptureTime; - private UUID mRequestId; - private ScrollCaptureCallback mCaptureCallback; + /** Returns a bitmap containing the combinded result. */ + public Bitmap toBitmap() { + return mImageTileSet.toBitmap(); + } + + public Bitmap toBitmap(Rect bounds) { + return mImageTileSet.toBitmap(bounds); + } - public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor, - ImageExporter exporter) { + /** Releases image resources from the screenshot. */ + public void release() { + Log.d(TAG, "LongScreenshot :: release()"); + mImageTileSet.clear(); + mSession.release(); + } + + public int getLeft() { + return mImageTileSet.getLeft(); + } + + public int getTop() { + return mImageTileSet.getTop(); + } + + public int getBottom() { + return mImageTileSet.getBottom(); + } + + public int getWidth() { + return mImageTileSet.getWidth(); + } + + public int getHeight() { + return mImageTileSet.getHeight(); + } + + /** @return the height of the visible area of the scrolling page, in pixels */ + public int getPageHeight() { + return mSession.getPageHeight(); + } + + @Override + public String toString() { + return "LongScreenshot{w=" + mImageTileSet.getWidth() + + ", h=" + mImageTileSet.getHeight() + "}"; + } + + public Drawable getDrawable() { + return mImageTileSet.getDrawable(); + } + } + + ScrollCaptureController(Context context, Executor bgExecutor, IWindowManager wms) { mContext = context; - mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; - mImageExporter = exporter; mImageTileSet = new ImageTileSet(context.getMainThreadHandler()); + mClient = new ScrollCaptureClient(mContext, wms); } /** - * Run scroll capture! + * Run scroll capture. Performs a batch capture, collecting image tiles. * - * @param connection connection to the remote window to be used - * @param callback request callback to report back to the service + * @param response a scroll capture response from a previous request which is + * {@link ScrollCaptureResponse#isConnected() connected}. + * @return a future ImageTile set containing the result */ - public void start(Connection connection, ScrollCaptureCallback callback) { - mCaptureTime = ZonedDateTime.now(); - mRequestId = UUID.randomUUID(); - mCaptureCallback = callback; - - float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), - SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); - connection.start(this::startCapture, maxPages); + ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) { + Log.d(TAG, "run: " + response); + return CallbackToFutureAdapter.getFuture(completer -> { + Log.d(TAG, "getFuture(ImageTileSet) "); + mCaptureCompleter = completer; + mBgExecutor.execute(() -> { + Log.d(TAG, "bgExecutor.execute"); + float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), + SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); + Log.d(TAG, "client start, maxPages=" + maxPages); + mSessionFuture = mClient.start(response, maxPages); + mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor()); + }); + return "<batch scroll capture>"; + }); + } + + private void onStartComplete() { + try { + mSession = mSessionFuture.get(); + Log.d(TAG, "got session " + mSession); + requestNextTile(0); + } catch (InterruptedException | ExecutionException e) { + // Failure to start, propagate to caller + Log.d(TAG, "session start failed!"); + mCaptureCompleter.setException(e); + } + } + + private void requestNextTile(int topPx) { + Log.d(TAG, "requestNextTile: " + topPx); + mTileFuture = mSession.requestTile(topPx); + mTileFuture.addListener(() -> { + try { + Log.d(TAG, "onCaptureResult"); + onCaptureResult(mTileFuture.get()); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "requestTile failed!", e); + mCaptureCompleter.setException(e); + } + }, mContext.getMainExecutor()); } private void onCaptureResult(CaptureResult result) { @@ -146,49 +247,24 @@ public class ScrollCaptureController { } if (finish) { - Session session = mSession; - mSession = null; Log.d(TAG, "Stop."); - mUiExecutor.execute(() -> afterCaptureComplete(session)); + finishCapture(); return; } int nextTop = (mScrollingUp) ? result.captured.top - mSession.getTileHeight() : result.captured.bottom; - Log.d(TAG, "requestTile: " + nextTop); - mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult); - } - - private void startCapture(Session session) { - mSession = session; - session.requestTile(0, this::onCaptureResult); + requestNextTile(nextTop); } - @UiThread - void afterCaptureComplete(Session session) { - Log.d(TAG, "afterCaptureComplete"); - - if (mImageTileSet.isEmpty()) { - mCaptureCallback.onError(); - } else { - mCaptureCallback.onComplete(mImageTileSet, session.getPageHeight()); - } + private void finishCapture() { + Log.d(TAG, "finishCapture()"); + mEndFuture = mSession.end(); + mEndFuture.addListener(() -> { + Log.d(TAG, "endCapture completed"); + // Provide result to caller and complete the top-level future + // Caller is responsible for releasing this resource (ImageReader/HardwareBuffers) + mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet)); + }, mContext.getMainExecutor()); } - - /** - * Callback for image capture completion or error. - */ - public interface ScrollCaptureCallback { - void onComplete(ImageTileSet imageTileSet, int pageHeight); - void onError(); - } - - /** - * Callback for image export completion or error. - */ - public interface ExportCallback { - void onExportComplete(Uri outputUri); - void onError(); - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 34b29ca9721b..2856ebb4066d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.pm.UserInfo; import android.net.Uri; import android.os.Handler; +import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -69,8 +70,10 @@ import com.android.systemui.statusbar.policy.RemoteInputView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; import dagger.Lazy; @@ -630,24 +633,17 @@ public class NotificationRemoteInputManager implements Dumpable { Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); if (remoteInputText != null || uri != null) { - RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[]) - sbn.getNotification().extras.getParcelableArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); - RemoteInputHistoryItem[] newHistoryItems; - - if (oldHistoryItems == null) { - newHistoryItems = new RemoteInputHistoryItem[1]; - } else { - newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1]; - System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length); - } - RemoteInputHistoryItem newItem; - if (uri != null) { - newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText); - } else { - newItem = new RemoteInputHistoryItem(remoteInputText); - } - newHistoryItems[0] = newItem; + RemoteInputHistoryItem newItem = uri != null + ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) + : new RemoteInputHistoryItem(remoteInputText); + Parcelable[] oldHistoryItems = sbn.getNotification().extras + .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null + ? Stream.concat( + Stream.of(newItem), + Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p)) + .toArray(RemoteInputHistoryItem[]::new) + : new RemoteInputHistoryItem[] { newItem }; b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index dbd8580b751e..5f93f4807c73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -41,11 +41,11 @@ import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.app.Person; import android.app.RemoteInput; -import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.pm.ShortcutInfo; import android.net.Uri; import android.os.Bundle; +import android.os.Parcelable; import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; @@ -534,8 +534,8 @@ public final class NotificationEntry extends ListEntry { return false; } Bundle extras = mSbn.getNotification().extras; - RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + Parcelable[] replyTexts = + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index 50bbc38eddf3..3f7b8aff7c51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -104,7 +104,7 @@ public class ActivatableNotificationViewController @Override public boolean onTouch(View v, MotionEvent ev) { - boolean result; + boolean result = false; if (mBlockNextTouch) { mBlockNextTouch = false; return true; @@ -112,16 +112,20 @@ public class ActivatableNotificationViewController if (ev.getAction() == MotionEvent.ACTION_UP) { mView.setLastActionUpTime(SystemClock.uptimeMillis()); } - if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled() - && mView.isInteractive()) { + // With a11y, just do nothing. + if (mAccessibilityManager.isTouchExplorationEnabled()) { + return false; + } + if (mNeedsDimming && mView.isInteractive()) { if (mNeedsDimming && !mView.isDimmed()) { // We're actually dimmed, but our content isn't dimmable, // let's ensure we have a ripple return false; } result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight()); - } else { - return false; + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + // If this is a false tap, capture the even so it doesn't result in a click. + return mFalsingManager.isFalseTap(true, 0.1); } return result; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 046fbd5b616b..6cf5c303149c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -77,6 +77,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; @@ -215,6 +216,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationEntry mEntry; private String mAppName; + private FalsingManager mFalsingManager; private FalsingCollector mFalsingCollector; /** @@ -887,6 +889,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // Other parts of the system may intercept and handle all the falsing. + // Otherwise, if we see motion and follow-on events, try to classify them as a tap. + if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { + mFalsingManager.isFalseTap(true, 0.3); + } + return super.onInterceptTouchEvent(ev); + } + + @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() != MotionEvent.ACTION_DOWN || !isChildInGroup() || isGroupExpanded()) { @@ -1569,6 +1581,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, CoordinateOnClickListener onFeedbackClickListener, + FalsingManager falsingManager, FalsingCollector falsingCollector, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, @@ -1594,6 +1607,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; setOnFeedbackClickListener(onFeedbackClickListener); + mFalsingManager = falsingManager; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 0d0e97eae519..c9fcdac8e45f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -26,6 +26,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -78,6 +79,7 @@ public class ExpandableNotificationRowController implements NodeController { private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; private final OnUserInteractionCallback mOnUserInteractionCallback; + private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; @@ -104,6 +106,7 @@ public class ExpandableNotificationRowController implements NodeController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, + FalsingManager falsingManager, FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional) { @@ -125,6 +128,7 @@ public class ExpandableNotificationRowController implements NodeController { mStatusBarStateController = statusBarStateController; mNotificationGutsManager = notificationGutsManager; mOnUserInteractionCallback = onUserInteractionCallback; + mFalsingManager = falsingManager; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; mFalsingCollector = falsingCollector; @@ -150,6 +154,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnExpandClickListener, mMediaManager, mOnFeedbackClickListener, + mFalsingManager, mFalsingCollector, mStatusBarStateController, mPeopleNotificationIdentifier, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index ca6e53d2ec04..acb3e5783525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3386,6 +3386,9 @@ public class NotificationPanelViewController extends PanelViewController { return new TouchHandler() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { + return true; + } if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) { return false; } @@ -3413,6 +3416,12 @@ public class NotificationPanelViewController extends PanelViewController { @Override public boolean onTouch(View v, MotionEvent event) { + final boolean showingOrAnimatingAltAuth = + mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating(); + if (showingOrAnimatingAltAuth) { + mStatusBarKeyguardViewManager.resetAlternateAuth(); + } + if (mBlockTouches || (mQsFullyExpanded && mQs != null && mQs.disallowPanelTouches())) { return false; @@ -3464,7 +3473,7 @@ public class NotificationPanelViewController extends PanelViewController { handled = true; } handled |= super.onTouch(v, event); - return !mDozing || mPulsing || handled; + return !mDozing || mPulsing || handled || showingOrAnimatingAltAuth; } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 3ac69378d7d1..ed4f32492c9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -453,7 +453,8 @@ public abstract class PanelViewController { // We need to collapse the panel since we peeked to the small height. mView.postOnAnimation(mPostCollapseRunnable); } - } else if (!mStatusBar.isBouncerShowing()) { + } else if (!mStatusBar.isBouncerShowing() + && !mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { boolean expands = onEmptySpaceClick(mInitialTouchX); onTrackingStopped(expands); } @@ -1394,13 +1395,8 @@ public abstract class PanelViewController { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mStatusBarKeyguardViewManager.isBouncerShowing() - && mFalsingManager.isFalseTap(true, 0.5)) { - endMotionEvent(event, x, y, true /* forceCancel */); - } else { - addMovement(event); - endMotionEvent(event, x, y, false /* forceCancel */); - } + addMovement(event); + endMotionEvent(event, x, y, false /* forceCancel */); InteractionJankMonitor monitor = InteractionJankMonitor.getInstance(); if (event.getActionMasked() == MotionEvent.ACTION_UP) { monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 9b8b7160c95c..b82863e75fe6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -88,6 +88,17 @@ public enum ScrimState { } }, + AUTH_SCRIMMED { + @Override + public void prepare(ScrimState previousState) { + mFrontTint = Color.BLACK; + + mBehindAlpha = 0f; + mFrontAlpha = .66f; + mBubbleAlpha = 0f; + } + }, + /** * Showing password challenge on the keyguard. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 117921dd860c..5045e95dd53f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -662,6 +662,16 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged"); } }; + + + private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener = + new FalsingManager.FalsingBeliefListener() { + @Override + public void onFalse() { + mStatusBarKeyguardViewManager.reset(true); + } + }; + private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -1000,6 +1010,8 @@ public class StatusBar extends SystemUI implements DemoMode, mInitController.addPostInitTask( () -> setUpDisableFlags(disabledFlags1, disabledFlags2)); + mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener); + mPluginManager.addPluginListener( new PluginListener<OverlayPlugin>() { private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>(); @@ -4147,7 +4159,7 @@ public class StatusBar extends SystemUI implements DemoMode, } @VisibleForTesting - void updateScrimController() { + public void updateScrimController() { Trace.beginSection("StatusBar#updateScrimController"); // We don't want to end up in KEYGUARD state when we're unlocking with @@ -4163,7 +4175,9 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); - if (mBouncerShowing) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuth()) { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + } else if (mBouncerShowing) { // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 01ada0f4c86c..81e24cc25aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -38,6 +38,7 @@ import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import android.widget.FrameLayout; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; @@ -199,6 +200,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; private KeyguardBypassController mBypassController; + @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -271,6 +273,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb registerListeners(); } + public void setAlternateAuthInterceptor(@Nullable AlternateAuthInterceptor authInterceptor) { + mAlternateAuthInterceptor = authInterceptor; + } + private void registerListeners() { mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); @@ -434,11 +440,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. - mAfterKeyguardGoneAction = null; - if (mKeyguardGoneCancelAction != null) { - mKeyguardGoneCancelAction.run(); - mKeyguardGoneCancelAction = null; - } + cancelPostAuthActions(); } mBouncer.hide(destroyView); cancelPendingWakeupAction(); @@ -474,6 +476,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } + if (mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.showAlternativeAuthMethod()) { + mStatusBar.updateScrimController(); + mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; + return; + } + if (!afterKeyguardGone) { mBouncer.showWithDismissAction(r, cancelAction); } else { @@ -508,11 +518,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else { showBouncerOrKeyguard(hideBouncerWhenShowing); } + resetAlternateAuth(); mKeyguardUpdateManager.sendKeyguardReset(); updateStates(); } } + /** + * Stop showing any alternate auth methods + */ + public void resetAlternateAuth() { + if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.reset()) { + mStatusBar.updateScrimController(); + } + } + @Override public void onStartedWakingUp() { mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() @@ -834,6 +854,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer.isFullscreenBouncer(); } + /** + * Clear out any potential actions that were saved to run when the device is unlocked + */ + public void cancelPostAuthActions() { + if (bouncerIsOrWillBeShowing()) { + return; // allow bouncer to trigger saved actions + } + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } + private long getNavBarShowDelay() { if (mKeyguardStateController.isKeyguardFadingAway()) { return mKeyguardStateController.getKeyguardFadingAwayDelay(); @@ -1063,6 +1097,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.dump(pw); } + + if (mAlternateAuthInterceptor != null) { + pw.println("AltAuthInterceptor: "); + mAlternateAuthInterceptor.dump(pw); + } } @Override @@ -1079,6 +1118,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer; } + public boolean isShowingAlternativeAuth() { + return mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.isShowingAlternativeAuth(); + } + + public boolean isShowingAlternativeAuthOrAnimating() { + return mAlternateAuthInterceptor != null + && (mAlternateAuthInterceptor.isShowingAlternativeAuth() + || mAlternateAuthInterceptor.isAnimating()); + } + private static class DismissWithActionRequest { final OnDismissAction dismissAction; final Runnable cancelAction; @@ -1093,4 +1143,36 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb this.message = message; } } + + /** + * Delegate used to send show/reset events to an alternate authentication method instead of the + * bouncer. + */ + public interface AlternateAuthInterceptor { + /** + * @return whether alternative auth method was newly shown + */ + boolean showAlternativeAuthMethod(); + + /** + * reset the state to the default (only keyguard showing, no auth methods showing) + * @return whether alternative auth method was newly hidden + */ + boolean reset(); + + /** + * @return true if alternative auth method is showing + */ + boolean isShowingAlternativeAuth(); + + /** + * print information for the alternate auth interceptor registered + */ + void dump(PrintWriter pw); + + /** + * @return true if the new auth method is currently animating in or out. + */ + boolean isAnimating(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java index e8331a176134..e76b8035cd59 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; import android.app.admin.DeviceAdminInfo; +import android.content.ComponentName; import android.graphics.drawable.Drawable; import com.android.systemui.Dumpable; @@ -33,6 +34,10 @@ public interface SecurityController extends CallbackController<SecurityControlle String getProfileOwnerName(); CharSequence getDeviceOwnerOrganizationName(); CharSequence getWorkProfileOrganizationName(); + /** Device owner component even if not on this user. **/ + ComponentName getDeviceOwnerComponentOnAnyUser(); + /** Device owner type for a device owner. **/ + int getDeviceOwnerType(ComponentName admin); boolean isNetworkLoggingEnabled(); boolean isVpnEnabled(); boolean isVpnRestricted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 5638503be198..4afb86b1a810 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,9 +15,11 @@ */ package com.android.systemui.statusbar.policy; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -225,6 +227,18 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi } @Override + @Nullable + public ComponentName getDeviceOwnerComponentOnAnyUser() { + return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser(); + } + + @Override + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + return mDevicePolicyManager.getDeviceOwnerType(admin); + } + + @Override public boolean isNetworkLoggingEnabled() { return mDevicePolicyManager.isNetworkLoggingEnabled(null); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 62722778384b..096ce0f9de27 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -41,8 +41,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -96,9 +94,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardMessageArea mKeyguardMessageArea; @Mock private ConfigurationController mConfigurationController; - @Mock - private KeyguardViewController mKeyguardViewController; - private FalsingManager mFalsingManager = new FalsingManagerFake(); private KeyguardSecurityContainerController mKeyguardSecurityContainerController; private KeyguardPasswordViewController mKeyguardPasswordViewController; @@ -124,7 +119,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, - mConfigurationController, mKeyguardViewController, mFalsingManager) + mConfigurationController) .create(mSecurityCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index d6f4958942dd..be110fcf70ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -44,8 +44,10 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -91,6 +93,10 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; + @Mock private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback; private FakeExecutor mFgExecutor; @@ -129,7 +135,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mWindowManager, mStatusBarStateController, mFgExecutor, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 480b33556b27..65f0f7b87cf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,9 +28,11 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import org.junit.Before; import org.junit.Test; @@ -51,24 +55,34 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private StatusBar mStatusBar; + @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; private UdfpsKeyguardViewController mController; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - private StatusBarStateController.StateListener mParentListener; - private StatusBarStateController.StateListener mDozeListener; + private StatusBarStateController.StateListener mParentStatusBarStateListener; + private StatusBarStateController.StateListener mStatusBarStateListener; @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor; private StatusBar.ExpansionChangedListener mExpansionListener; + @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor> + mAltAuthInterceptorCaptor; + private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mController = new UdfpsKeyguardViewController( mView, mStatusBarStateController, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); } @Test @@ -86,7 +100,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Test public void testViewControllerQueriesSBStateOnAttached() { mController.onViewAttached(); - verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController, times(2)).getState(); verify(mStatusBarStateController).getDozeAmount(); final float dozeAmount = .88f; @@ -106,8 +120,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { captureExpansionListener(); mController.onViewDetached(); - verify(mStatusBarStateController).removeCallback(mParentListener); - verify(mStatusBarStateController).removeCallback(mDozeListener); + verify(mStatusBarStateController).removeCallback(mParentStatusBarStateListener); + verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); verify(mStatusBar).removeExpansionChangedListener(mExpansionListener); } @@ -118,21 +132,88 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { final float linear = .55f; final float eased = .65f; - mDozeListener.onDozeAmountChanged(linear, eased); + mStatusBarStateListener.onDozeAmountChanged(linear, eased); verify(mView).onDozeAmountChanged(linear, eased); } + @Test + public void testShouldNotPauseAuthOnKeyguard() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testOverrideShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + assertTrue(mController.shouldPauseAuth()); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // force show + assertFalse(mController.shouldPauseAuth()); + assertTrue(mAltAuthInterceptor.isShowingAlternativeAuth()); + + mAltAuthInterceptor.reset(); // stop force show + assertTrue(mController.shouldPauseAuth()); + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + @Test + public void testOnDetachedStateReset() { + // GIVEN view is attached, alt auth is force being shown + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // alt auth force show + + // WHEN view is detached + mController.onViewDetached(); + + // THEN alt auth state reports not showing + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + private void sendStatusBarStateChanged(int statusBarState) { + mStatusBarStateListener.onStateChanged(statusBarState); + mParentStatusBarStateListener.onStateChanged(statusBarState); + } + private void captureStatusBarStateListeners() { verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture()); List<StatusBarStateController.StateListener> stateListeners = mStateListenerCaptor.getAllValues(); - mParentListener = stateListeners.get(0); - mDozeListener = stateListeners.get(1); + mParentStatusBarStateListener = stateListeners.get(0); + mStatusBarStateListener = stateListeners.get(1); } private void captureExpansionListener() { verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture()); mExpansionListener = mExpansionListenerCaptor.getValue(); } + + private void captureAltAuthInterceptor() { + verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor( + mAltAuthInterceptorCaptor.capture()); + mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 862e3747f602..5422ae831de3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -14,6 +14,9 @@ package com.android.systemui.qs; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -24,6 +27,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; +import android.content.DialogInterface; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; @@ -74,6 +79,8 @@ public class QSSecurityFooterTest extends SysuiTestCase { private final String VPN_PACKAGE = "TestVPN"; private final String VPN_PACKAGE_2 = "TestVPN 2"; private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App"; + private static final ComponentName DEVICE_OWNER_COMPONENT = + new ComponentName("TestDPC", "Test"); private ViewGroup mRootView; private TextView mFooterText; @@ -101,6 +108,11 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterIcon = mRootView.findViewById(R.id.footer_icon); mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooter.setHostEnvironment(null); + + when(mSecurityController.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); } @Test @@ -148,6 +160,27 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testManagedFinancedDeviceWithOwnerName() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + mFooter.refreshState(); + + TestableLooper.get(this).processAllMessages(); + assertEquals(mContext.getString( + R.string.quick_settings_financed_disclosure_named_management, + MANAGING_ORGANIZATION), mFooterText.getText()); + assertEquals(View.VISIBLE, mRootView.getVisibility()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + } + + @Test public void testManagedDemoMode() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null); @@ -383,6 +416,25 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testGetManagementTitleForNonFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + + assertEquals(mContext.getString(R.string.monitoring_title_device_owned), + mFooter.getManagementTitle(MANAGING_ORGANIZATION)); + } + + @Test + public void testGetManagementTitleForFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + assertEquals(mContext.getString(R.string.monitoring_title_financed_device, + MANAGING_ORGANIZATION), + mFooter.getManagementTitle(MANAGING_ORGANIZATION)); + } + + @Test public void testGetManagementMessage_noManagement() { assertEquals(null, mFooter.getManagementMessage( /* isDeviceManaged= */ false, @@ -409,6 +461,21 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test + public void testGetManagementMessage_deviceOwner_asFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management, + MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), + mFooter.getManagementMessage( + /* isDeviceManaged= */ true, + MANAGING_ORGANIZATION, + /* isProfileOwnerOfOrganizationOwnedDevice= */ false, + /* workProfileOrganizationName= */ null)); + } + + @Test public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() { assertEquals(mContext.getString(R.string.monitoring_description_named_management, MANAGING_ORGANIZATION), @@ -587,6 +654,34 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText()); } + @Test + public void testCreateDialogViewForFinancedDevice() { + when(mSecurityController.isDeviceManaged()).thenReturn(true); + when(mSecurityController.getDeviceOwnerOrganizationName()) + .thenReturn(MANAGING_ORGANIZATION); + when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + + // Initialize AlertDialog which sets the text for the negative button, which is used when + // creating the dialog for a financed device. + mFooter.showDeviceMonitoringDialog(); + // The above statement would display the Quick Settings dialog which requires user input, + // so simulate the press to continue with the unit test (otherwise, it is stuck). + mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE); + View view = mFooter.createDialogView(); + + TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle); + assertEquals(View.VISIBLE, managementSubtitle.getVisibility()); + assertEquals(mContext.getString(R.string.monitoring_title_financed_device, + MANAGING_ORGANIZATION), managementSubtitle.getText()); + TextView managementMessage = view.findViewById(R.id.device_management_warning); + assertEquals(View.VISIBLE, managementMessage.getVisibility()); + assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management, + MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), managementMessage.getText()); + assertEquals(mContext.getString(R.string.monitoring_button_view_policies), + mFooter.getSettingsButton()); + } + private CharSequence addLink(CharSequence description) { final SpannableStringBuilder message = new SpannableStringBuilder(); message.append(description); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java index 9e62a6263a43..63f7c9755782 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; +import android.os.CancellationSignal; import android.os.ICancellationSignal; import android.os.RemoteException; import android.view.IScrollCaptureCallbacks; @@ -37,19 +38,15 @@ import android.view.Surface; class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE}; private IScrollCaptureCallbacks mCallbacks; - private Surface mSurface; private Paint mPaint; private int mNextColor; private HwuiContext mHwuiContext; - - FakeScrollCaptureConnection(IScrollCaptureCallbacks cb) { - mCallbacks = cb; - } + private CancellationSignal mCancellationSignal; @Override - public ICancellationSignal startCapture(Surface surface) { - mSurface = surface; - mHwuiContext = new HwuiContext(false, surface); + public ICancellationSignal startCapture(Surface surface, IScrollCaptureCallbacks callbacks) { + mCallbacks = callbacks; + mHwuiContext = new HwuiContext(surface); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); try { @@ -57,7 +54,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override @@ -72,7 +71,9 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override @@ -83,15 +84,15 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { e.rethrowAsRuntimeException(); } finally { mHwuiContext.destroy(); - mSurface = null; mCallbacks = null; } - return null; + ICancellationSignal signal = CancellationSignal.createTransport(); + mCancellationSignal = CancellationSignal.fromTransport(signal); + return signal; } @Override public void close() throws RemoteException { - } // From android.view.Surface, but issues render requests synchronously with waitForPresent(true) @@ -99,21 +100,16 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final RenderNode mRenderNode; private final HardwareRenderer mHardwareRenderer; private RecordingCanvas mCanvas; - private final boolean mIsWideColorGamut; - HwuiContext(boolean isWideColorGamut, Surface surface) { + HwuiContext(Surface surface) { mRenderNode = RenderNode.create("HwuiCanvas", null); mRenderNode.setClipToBounds(false); mRenderNode.setForceDarkAllowed(false); - mIsWideColorGamut = isWideColorGamut; mHardwareRenderer = new HardwareRenderer(); mHardwareRenderer.setContentRoot(mRenderNode); mHardwareRenderer.setSurface(surface, true); - mHardwareRenderer.setColorMode( - isWideColorGamut - ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT - : ActivityInfo.COLOR_MODE_DEFAULT); + mHardwareRenderer.setColorMode(ActivityInfo.COLOR_MODE_DEFAULT); mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f); mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f); } @@ -142,9 +138,5 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { void destroy() { mHardwareRenderer.destroy(); } - - boolean isWideColorGamut() { - return mIsWideColorGamut; - } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java index 802b462ec10e..cf7dc20160ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java @@ -16,15 +16,15 @@ package com.android.systemui.screenshot; -import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; import static java.util.Objects.requireNonNull; @@ -34,7 +34,7 @@ import android.hardware.display.DisplayManager; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.view.Display; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; @@ -43,30 +43,29 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; -import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; +import com.google.common.util.concurrent.ListenableFuture; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.Spy; import org.mockito.stubbing.Answer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + @SmallTest @RunWith(AndroidTestingRunner.class) public class ScrollCaptureClientTest extends SysuiTestCase { - private static final float MAX_PAGES = 3f; + private static final float MAX_PAGES = 3.0f; private Context mContext; private IWindowManager mWm; - @Spy private TestableConsumer<Session> mSessionConsumer; - @Spy private TestableConsumer<Connection> mConnectionConsumer; - @Spy private TestableConsumer<CaptureResult> mResultConsumer; - @Mock private Runnable mRunnable; - @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -79,46 +78,50 @@ public class ScrollCaptureClientTest extends SysuiTestCase { } @Test - public void testBasicClientFlow() throws RemoteException { + public void testDetectAndConnect() + throws RemoteException, InterruptedException, ExecutionException, TimeoutException { doAnswer((Answer<Void>) invocation -> { - IScrollCaptureCallbacks cb = invocation.getArgument(3); - cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() + IScrollCaptureResponseListener listener = invocation.getArgument(3); + listener.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() .setBoundsInWindow(new Rect(0, 0, 100, 100)) .setWindowBounds(new Rect(0, 0, 100, 100)) - .setConnection(new FakeScrollCaptureConnection(cb)) + .setConnection(new FakeScrollCaptureConnection()) .build()); return null; }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(), - /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class)); + /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class)); // Create client ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm); - client.request(Display.DEFAULT_DISPLAY, mConnectionConsumer); - verify(mConnectionConsumer, timeout(100)).accept(any(Connection.class)); + // Request scroll capture + ListenableFuture<ScrollCaptureResponse> requestFuture = + client.request(Display.DEFAULT_DISPLAY); + assertNotNull(requestFuture.get(100, TimeUnit.MILLISECONDS)); - Connection conn = mConnectionConsumer.getValue(); + ScrollCaptureResponse response = requestFuture.get(); + assertTrue(response.isConnected()); - conn.start(mSessionConsumer, MAX_PAGES); - verify(mSessionConsumer, timeout(100)).accept(any(Session.class)); + // Start a session + ListenableFuture<Session> startFuture = client.start(response, MAX_PAGES); + assertNotNull(startFuture.get(100, TimeUnit.MILLISECONDS)); - Session session = mSessionConsumer.getValue(); + Session session = startFuture.get(); Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight()); - session.requestTile(0, mResultConsumer); - verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class)); + // Request a tile + ListenableFuture<CaptureResult> tileFuture = session.requestTile(0); + assertNotNull(tileFuture.get(100, TimeUnit.MILLISECONDS)); - CaptureResult result = mResultConsumer.getValue(); - assertThat(result.requested).isEqualTo(request); - assertThat(result.captured).isEqualTo(result.requested); - assertThat(result.image).isNotNull(); + CaptureResult result = tileFuture.get(); + assertEquals(request, result.requested); + assertEquals(result.requested, result.captured); + assertNotNull(result.image); - session.end(mRunnable); - verify(mRunnable, timeout(100)).run(); - - // TODO verify image - // TODO test threading - // TODO test failures + // End the session + ListenableFuture<Void> endFuture = session.end(); + CountDownLatch latch = new CountDownLatch(1); + endFuture.addListener(latch::countDown, Runnable::run); + assertTrue(latch.await(100, TimeUnit.MILLISECONDS)); } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java index 6564d588f4ea..06b39abd1d0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java @@ -16,15 +16,16 @@ package com.android.systemui.screenshot; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.Intent; -import android.graphics.Rect; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Log; import android.view.Display; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindowManager; import android.view.ScrollCaptureResponse; import android.view.WindowManagerGlobal; @@ -45,8 +46,9 @@ import java.util.concurrent.TimeUnit; */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class ScrollCaptureTest extends SysuiTestCase { - private static final String TAG = "ScrollCaptureTest"; +public class ScrollCaptureFrameworkSmokeTest extends SysuiTestCase { + private static final String TAG = "ScrollCaptureFrameworkSmokeTest"; + private volatile ScrollCaptureResponse mResponse; /** * Verifies that a request traverses from SystemUI, to WindowManager and to the app process and @@ -64,34 +66,23 @@ public class ScrollCaptureTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); try { wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1, - new IScrollCaptureCallbacks.Stub() { + new IScrollCaptureResponseListener.Stub() { @Override - public void onScrollCaptureResponse(ScrollCaptureResponse response) + public void onScrollCaptureResponse( + ScrollCaptureResponse response) throws RemoteException { - Log.d(TAG, "onScrollCaptureResponse: " + response); + mResponse = response; latch.countDown(); } - - @Override - public void onCaptureStarted() { - } - - @Override - public void onImageRequestCompleted(int i, Rect rect) - throws RemoteException { - - } - - @Override - public void onCaptureEnded() throws RemoteException { - - } - }); } catch (RemoteException e) { Log.e(TAG, "request failed", e); fail("caught remote exception " + e); } latch.await(1000, TimeUnit.MILLISECONDS); + + assertNotNull(mResponse); + assertTrue(mResponse.isConnected()); + assertTrue(mResponse.getWindowTitle().contains(ScrollViewActivity.class.getSimpleName())); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 97313bafb8a8..950b95f41542 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -46,6 +46,7 @@ import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -260,6 +261,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mGutsManager, true, null, + new FalsingManagerFake(), new FalsingCollectorFake(), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index f3813fcaf6fc..b4a3393ab5db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -45,6 +45,7 @@ import android.widget.RemoteViews; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -429,6 +430,7 @@ public class NotificationTestHelper { mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), mock(ExpandableNotificationRow.CoordinateOnClickListener.class), + new FalsingManagerFake(), new FalsingCollectorFake(), mStatusBarStateController, mPeopleNotificationIdentifier, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 44184ee8eeed..ed87a4040022 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -62,6 +64,9 @@ import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class SecurityControllerTest extends SysuiTestCase { + private static final ComponentName DEVICE_OWNER_COMPONENT = + new ComponentName("com.android.foo", "bar"); + private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class); private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class); private final UserManager mUserManager = mock(UserManager.class); @@ -127,6 +132,22 @@ public class SecurityControllerTest extends SysuiTestCase { } @Test + public void testGetDeviceOwnerComponentOnAnyUser() { + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + assertEquals(mSecurityController.getDeviceOwnerComponentOnAnyUser(), + DEVICE_OWNER_COMPONENT); + } + + @Test + public void testGetDeviceOwnerType() { + when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + assertEquals(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT), + DEVICE_OWNER_TYPE_FINANCED); + } + + @Test public void testWorkAccount() throws Exception { assertFalse(mSecurityController.hasCACertInCurrentUser()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java index c0722a459929..3640bcd64c14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java @@ -15,6 +15,7 @@ package com.android.systemui.utils.leaks; import android.app.admin.DeviceAdminInfo; +import android.content.ComponentName; import android.graphics.drawable.Drawable; import android.testing.LeakCheck; @@ -68,6 +69,16 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa } @Override + public ComponentName getDeviceOwnerComponentOnAnyUser() { + return null; + } + + @Override + public int getDeviceOwnerType(ComponentName admin) { + return 0; + } + + @Override public boolean isNetworkLoggingEnabled() { return false; } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 5adbdff150ea..7276c78b398c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -60,6 +60,7 @@ import android.telephony.CellSignalStrengthNr; import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.DisconnectCause; +import android.telephony.LinkCapacityEstimate; import android.telephony.LocationAccessPolicy; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; @@ -321,6 +322,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private int[] mAllowedNetworkTypeReason; private long[] mAllowedNetworkTypeValue; + private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -351,6 +354,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( TelephonyCallback.EVENT_DATA_ENABLED_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED); } private boolean isLocationPermissionRequired(Set<Integer> events) { @@ -539,6 +544,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mPreciseDataConnectionStates, mNumPhones); cutListToSize(mBarringInfo, mNumPhones); cutListToSize(mPhysicalChannelConfigs, mNumPhones); + cutListToSize(mLinkCapacityEstimateLists, mNumPhones); return; } @@ -577,6 +583,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; + mLinkCapacityEstimateLists.add(i, new ArrayList<>()); } } @@ -640,6 +647,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mAllowedNetworkTypeValue = new long[numPhones]; mIsDataEnabled = new boolean[numPhones]; mDataEnabledReason = new int[numPhones]; + mLinkCapacityEstimateLists = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; @@ -675,6 +683,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; + mLinkCapacityEstimateLists.add(i, new ArrayList<>()); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1182,6 +1191,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if (events.contains( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) { + try { + if (mLinkCapacityEstimateLists.get(phoneId) != null) { + r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists + .get(phoneId)); + } + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } @@ -2492,6 +2512,42 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Notify that the link capacity estimate has changed. + * @param phoneId the phone id. + * @param subId the subscription id. + * @param linkCapacityEstimateList a list of {@link LinkCapacityEstimate} + */ + public void notifyLinkCapacityEstimateChanged(int phoneId, int subId, + List<LinkCapacityEstimate> linkCapacityEstimateList) { + if (!checkNotifyPermission("notifyLinkCapacityEstimateChanged()")) { + return; + } + + if (VDBG) { + log("notifyLinkCapacityEstimateChanged: linkCapacityEstimateList =" + + linkCapacityEstimateList); + } + + synchronized (mRecords) { + if (validatePhoneId(phoneId)) { + mLinkCapacityEstimateLists.set(phoneId, linkCapacityEstimateList); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + handleRemoveListLocked(); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); @@ -2538,6 +2594,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]); pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]); + pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i)); pw.decreaseIndent(); } pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 14f4d02cbd48..3c445ae4b667 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER; import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE; import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; @@ -5597,6 +5598,14 @@ public final class ActiveServices { && instr.mHasBackgroundForegroundServiceStartsPermission) { return REASON_INSTR_BACKGROUND_FGS_PERMISSION; } + final long lastInvisibleTime = app.mState.getLastInvisibleTime(); + if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) { + final long sinceLastInvisible = SystemClock.elapsedRealtime() + - lastInvisibleTime; + if (sinceLastInvisible < mAm.mConstants.mFgToBgFgsGraceDuration) { + return REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD; + } + } } } return null; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index ba8f1906b0e1..5859cea2d284 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -95,6 +95,7 @@ final class ActivityManagerConstants extends ContentObserver { "process_crash_count_reset_interval"; static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit"; static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration"; + static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration"; private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; @@ -133,6 +134,7 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_PROCESS_CRASH_COUNT_RESET_INTERVAL = 12 * 60 * 60 * 1000; private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12; private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 10 * 1000; + private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000; // Flag stored in the DeviceConfig API. @@ -388,6 +390,12 @@ final class ActivityManagerConstants extends ContentObserver { */ volatile long mBootTimeTempAllowlistDuration = DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION; + /** + * The grace period in milliseconds to allow a process to start FGS from background after + * switching from foreground to background; currently it's only applicable to its activities. + */ + volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION; + private final ActivityManagerService mService; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -575,6 +583,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION: updateBootTimeTempAllowListDuration(); break; + case KEY_FG_TO_BG_FGS_GRACE_DURATION: + updateFgToBgFgsGraceDuration(); + break; default: break; } @@ -851,6 +862,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION); } + private void updateFgToBgFgsGraceDuration() { + mFgToBgFgsGraceDuration = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FG_TO_BG_FGS_GRACE_DURATION, + DEFAULT_FG_TO_BG_FGS_GRACE_DURATION); + } + private void updateImperceptibleKillExemptions() { IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear(); IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages); @@ -1051,6 +1069,8 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(MAX_PHANTOM_PROCESSES); pw.print(" "); pw.print(KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION); pw.print("="); pw.println(mBootTimeTempAllowlistDuration); + pw.print(" "); pw.print(KEY_FG_TO_BG_FGS_GRACE_DURATION); pw.print("="); + pw.println(mFgToBgFgsGraceDuration); pw.println(); if (mOverrideMaxCachedProcesses >= 0) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 939d35fdb5ef..5ae65ef0e4be 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1367,6 +1367,7 @@ public final class OomAdjuster { ProcessRecord app; int adj; boolean foregroundActivities; + boolean mHasVisibleActivities; int procState; int schedGroup; int appUid; @@ -1375,10 +1376,12 @@ public final class OomAdjuster { ProcessStateRecord mState; void initialize(ProcessRecord app, int adj, boolean foregroundActivities, - int procState, int schedGroup, int appUid, int logUid, int processStateCurTop) { + boolean hasVisibleActivities, int procState, int schedGroup, int appUid, + int logUid, int processStateCurTop) { this.app = app; this.adj = adj; this.foregroundActivities = foregroundActivities; + this.mHasVisibleActivities = hasVisibleActivities; this.procState = procState; this.schedGroup = schedGroup; this.appUid = appUid; @@ -1411,6 +1414,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = true; } @Override @@ -1436,6 +1440,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1468,6 +1473,7 @@ public final class OomAdjuster { mState.setCached(false); mState.setEmpty(false); foregroundActivities = true; + mHasVisibleActivities = false; } @Override @@ -1480,6 +1486,7 @@ public final class OomAdjuster { "Raise procstate to cached activity: " + app); } } + mHasVisibleActivities = false; } } @@ -1591,12 +1598,14 @@ public final class OomAdjuster { int capability = 0; boolean foregroundActivities = false; + boolean hasVisibleActivities = false; if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = ProcessList.SCHED_GROUP_TOP_APP; state.setAdjType("top-activity"); foregroundActivities = true; + hasVisibleActivities = true; procState = PROCESS_STATE_CUR_TOP; state.bumpAllowStartFgsState(PROCESS_STATE_TOP); if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { @@ -1672,11 +1681,12 @@ public final class OomAdjuster { // Examine all activities if not already foreground. if (!foregroundActivities && state.getCachedHasActivities()) { state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback, - adj, foregroundActivities, procState, schedGroup, appUid, logUid, - PROCESS_STATE_CUR_TOP); + adj, foregroundActivities, hasVisibleActivities, procState, schedGroup, + appUid, logUid, PROCESS_STATE_CUR_TOP); adj = state.getCachedAdj(); foregroundActivities = state.getCachedForegroundActivities(); + hasVisibleActivities = state.getCachedHasVisibleActivities(); procState = state.getCachedProcState(); schedGroup = state.getCachedSchedGroup(); } @@ -2450,6 +2460,7 @@ public final class OomAdjuster { state.setCurrentSchedulingGroup(schedGroup); state.setCurProcState(procState); state.setCurRawProcState(procState); + state.updateLastInvisibleTime(hasVisibleActivities); state.setHasForegroundActivities(foregroundActivities); state.setCompletedAdjSeq(mAdjSeq); state.setAllowStartFgs(); diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 6d783fc63901..d97d343a1960 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -43,6 +43,7 @@ import static android.os.Process.SYSTEM_UID; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ProcessRecord.TAG; +import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; import android.os.Binder; @@ -386,6 +387,16 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mReachable; + /** + * The most recent time when the last visible activity within this process became invisible. + * + * <p> It'll be set to 0 if there is never a visible activity, or Long.MAX_VALUE if there is + * any visible activities within this process at this moment.</p> + */ + @GuardedBy("mService") + @ElapsedRealtimeLong + private long mLastInvisibleTime; + // Below are the cached task info for OomAdjuster only private static final int VALUE_INVALID = -1; private static final int VALUE_FALSE = 0; @@ -1040,18 +1051,19 @@ final class ProcessStateRecord { @GuardedBy("mService") void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback, - int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid, - int logUid, int processCurTop) { + int adj, boolean foregroundActivities, boolean hasVisibleActivities, int procState, + int schedGroup, int appUid, int logUid, int processCurTop) { if (mCachedAdj != ProcessList.INVALID_ADJ) { return; } - callback.initialize(mApp, adj, foregroundActivities, procState, schedGroup, appUid, logUid, - processCurTop); + callback.initialize(mApp, adj, foregroundActivities, hasVisibleActivities, procState, + schedGroup, appUid, logUid, processCurTop); final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX, mApp.getWindowProcessController().computeOomAdjFromActivities(callback)); mCachedAdj = callback.adj; mCachedForegroundActivities = callback.foregroundActivities; + mCachedHasVisibleActivities = callback.mHasVisibleActivities ? VALUE_TRUE : VALUE_FALSE; mCachedProcState = callback.procState; mCachedSchedGroup = callback.schedGroup; @@ -1263,6 +1275,21 @@ final class ProcessStateRecord { return mAllowStartFgs; } + @GuardedBy("mService") + void updateLastInvisibleTime(boolean hasVisibleActivities) { + if (hasVisibleActivities) { + mLastInvisibleTime = Long.MAX_VALUE; + } else if (mLastInvisibleTime == Long.MAX_VALUE) { + mLastInvisibleTime = SystemClock.elapsedRealtime(); + } + } + + @GuardedBy("mService") + @ElapsedRealtimeLong + long getLastInvisibleTime() { + return mLastInvisibleTime; + } + @GuardedBy({"mService", "mProcLock"}) void dump(PrintWriter pw, String prefix, long nowUptime) { if (mReportedInteraction || mFgInteractionTime != 0) { @@ -1340,6 +1367,15 @@ final class ProcessStateRecord { TimeUtils.formatDuration(mLastTopTime, nowUptime, pw); pw.println(); } + if (mLastInvisibleTime > 0 && mLastInvisibleTime < Long.MAX_VALUE) { + pw.print(prefix); pw.print("lastInvisibleTime="); + final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); + final long currentTimeNow = System.currentTimeMillis(); + final long lastInvisibleCurrentTime = + currentTimeNow - elapsedRealtimeNow + mLastInvisibleTime; + TimeUtils.dumpTimeWithDelta(pw, lastInvisibleCurrentTime, currentTimeNow); + pw.println(); + } if (mHasStartedServices) { pw.print(prefix); pw.print("hasStartedServices="); pw.println(mHasStartedServices); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index dfec2e3e308f..0d50499bd02a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -67,7 +67,13 @@ public interface ServiceProvider { @NonNull List<FingerprintSensorPropertiesInternal> getSensorProperties(); - @NonNull + /** + * Returns the internal properties of the specified sensor, if owned by this provider. + * + * @param sensorId The ID of a fingerprint sensor, or -1 for any sensor. + * @return An object representing the internal properties of the specified sensor. + */ + @Nullable FingerprintSensorPropertiesInternal getSensorProperties(int sensorId); void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 598cc8992c2d..d798198782ea 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -16,6 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static android.hardware.fingerprint.FingerprintManager.SENSOR_ID_ANY; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -240,10 +242,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi return props; } - @NonNull + @Nullable @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) { - return mSensors.get(sensorId).getSensorProperties(); + if (mSensors.size() == 0) { + return null; + } else if (sensorId == SENSOR_ID_ANY) { + return mSensors.valueAt(0).getSensorProperties(); + } else { + final Sensor sensor = mSensors.get(sensorId); + return sensor != null ? sensor.getSensorProperties() : null; + } } private void scheduleLoadAuthenticatorIds(int sensorId) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index cc3b569a7377..50fdc2e8a856 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -516,7 +516,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider return properties; } - @NonNull + @Nullable @Override public FingerprintSensorPropertiesInternal getSensorProperties(int sensorId) { return mSensorProperties; diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index 7a5abf807fbb..702434ba07b7 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -127,13 +127,17 @@ public class DnsManager { private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; - public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { - final String mode = ConnectivityManager.getPrivateDnsMode(cr); + /** + * Get PrivateDnsConfig. + */ + public static PrivateDnsConfig getPrivateDnsConfig(Context context) { + final String mode = ConnectivityManager.getPrivateDnsMode(context); final boolean useTls = !TextUtils.isEmpty(mode) && !PRIVATE_DNS_MODE_OFF.equals(mode); if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) { - final String specifier = getStringSetting(cr, PRIVATE_DNS_SPECIFIER); + final String specifier = getStringSetting(context.getContentResolver(), + PRIVATE_DNS_SPECIFIER); return new PrivateDnsConfig(specifier, null); } @@ -268,7 +272,7 @@ public class DnsManager { } public PrivateDnsConfig getPrivateDnsConfig() { - return getPrivateDnsConfig(mContentResolver); + return getPrivateDnsConfig(mContext); } public void removeNetwork(Network network) { diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index 5e6b9f39b40a..2e51be39bfae 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -36,7 +36,7 @@ import android.text.TextUtils; import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.TrafficStatsConstants; +import com.android.net.module.util.NetworkStackConstants; import libcore.io.IoUtils; @@ -446,7 +446,7 @@ public class NetworkDiagnostics { int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort) throws ErrnoException, IOException { final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); try { mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol); } finally { @@ -745,7 +745,7 @@ public class NetworkDiagnostics { if (ensureMeasurementNecessary()) return; // No need to restore the tag, since this thread is only used for this measurement. - TrafficStats.getAndSetThreadStatsTag(TrafficStatsConstants.TAG_SYSTEM_PROBE); + TrafficStats.getAndSetThreadStatsTag(NetworkStackConstants.TAG_SYSTEM_PROBE); try (SSLSocket sslSocket = setupSSLSocket()) { sendDoTProbe(sslSocket); diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 217f1cd56598..a8b0994402e8 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -42,6 +42,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.SystemService; import com.android.server.policy.DeviceStatePolicyImpl; @@ -447,6 +448,9 @@ public final class DeviceStateManagerService extends SystemService { } } + FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED, + newState.getIdentifier(), !mCommittedState.isPresent()); + mCommittedState = Optional.of(newState); mPendingState = Optional.empty(); updatePendingStateLocked(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 370d921de2af..75f06e5e9088 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -90,7 +90,6 @@ import android.system.Os; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -2447,8 +2446,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub @Override public void removeOnLocalColorsChangedListener( - @NonNull ILocalWallpaperColorConsumer callback, int which, int userId, - int displayId) throws RemoteException { + @NonNull ILocalWallpaperColorConsumer callback, List<RectF> removeAreas, int which, + int userId, int displayId) throws RemoteException { if (which != FLAG_LOCK && which != FLAG_SYSTEM) { throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); } @@ -2457,43 +2456,45 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new SecurityException("calling user id does not match"); } final long identity = Binder.clearCallingIdentity(); - ArrayList<RectF> removeAreas = new ArrayList<>(); - ArrayList<Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>> - callbacksToRemove = new ArrayList<>(); + ArrayList<RectF> purgeAreas = new ArrayList<>(); + IBinder binder = callback.asBinder(); try { synchronized (mLock) { - ArraySet<RectF> areas = mLocalColorCallbackAreas.remove(callback.asBinder()); - mLocalColorCallbackDisplayId.remove(callback.asBinder()); - if (areas == null) areas = new ArraySet<>(); - for (RectF area : areas) { - RemoteCallbackList callbacks = mLocalColorAreaCallbacks.get(area); - if (callbacks == null) continue; - callbacksToRemove.add(new Pair<>(callbacks, callback)); - if (callbacks.getRegisteredCallbackCount() == 0) { - mLocalColorAreaCallbacks.remove(area); - removeAreas.add(area); - } - ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); - if (displayAreas != null) { - displayAreas.remove(area); + ArraySet<RectF> currentAreas = mLocalColorCallbackAreas.get(binder); + if (currentAreas == null) return; + currentAreas.removeAll(removeAreas); + if (currentAreas.size() == 0) { + mLocalColorCallbackDisplayId.remove(binder); + for (RectF removeArea : removeAreas) { + RemoteCallbackList<ILocalWallpaperColorConsumer> remotes = + mLocalColorAreaCallbacks.get(removeArea); + if (remotes == null) continue; + remotes.unregister(callback); + if (remotes.getRegisteredCallbackCount() == 0) { + mLocalColorAreaCallbacks.remove(removeArea); + purgeAreas.add(removeArea); + ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); + if (displayAreas != null) { + displayAreas.remove(removeArea); + if (displayAreas.size() == 0) { + mLocalColorDisplayIdAreas.remove(displayId); + } + } + } } } } - for (int i = 0; i < callbacksToRemove.size(); i++) { - Pair<RemoteCallbackList, ILocalWallpaperColorConsumer> - pair = callbacksToRemove.get(i); - pair.first.unregister(pair.second); - } + } catch (Exception e) { // ignore any exception } finally { Binder.restoreCallingIdentity(identity); } - if (removeAreas.size() == 0) return; + if (purgeAreas.size() == 0) return; IWallpaperEngine engine = getEngine(which, userId, displayId); if (engine == null) return; - engine.removeLocalColorsAreas(removeAreas); + engine.removeLocalColorsAreas(purgeAreas); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c830ba9b61dd..c5115b283f0a 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -65,6 +65,7 @@ import android.os.Trace; import android.service.voice.VoiceInteractionManagerInternal; import android.util.Slog; import android.view.RemoteAnimationDefinition; +import android.window.SizeConfigurationBuckets; import com.android.internal.app.AssistUtils; import com.android.internal.policy.IKeyguardDismissCallback; @@ -74,8 +75,6 @@ import com.android.server.Watchdog; import com.android.server.uri.NeededUriGrants; import com.android.server.vr.VrManagerInternal; -import java.util.Arrays; - /** * Server side implementation for the client activity to interact with system. * @@ -244,16 +243,14 @@ class ActivityClientController extends IActivityClientController.Stub { } @Override - public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { - ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s", - token, Arrays.toString(horizontalSizeConfiguration), - Arrays.toString(verticalSizeConfigurations)); + public void reportSizeConfigurations(IBinder token, + SizeConfigurationBuckets sizeConfigurations) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s", + token, sizeConfigurations); synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r != null) { - r.setSizeConfigurations(horizontalSizeConfiguration, verticalSizeConfigurations, - smallestSizeConfigurations); + r.setSizeConfigurations(sizeConfigurations); } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 89dac0552c9f..a909c6d119e8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -316,6 +316,7 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; import android.window.IRemoteTransition; +import android.window.SizeConfigurationBuckets; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -563,12 +564,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The locusId associated with this activity, if set. private LocusId mLocusId; - // These configurations are collected from application's resources based on size-sensitive - // qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800 - // and drawable-sw400dp will be added to both as 400. - private int[] mVerticalSizeConfigurations; - private int[] mHorizontalSizeConfigurations; - private int[] mSmallestSizeConfigurations; + private SizeConfigurationBuckets mSizeConfigurations; /** * The precomputed display insets for resolving configuration. It will be non-null if @@ -1081,11 +1077,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.println(prefix + "supportsEnterPipOnTaskSwitch: " + supportsEnterPipOnTaskSwitch); } - if (info.maxAspectRatio != 0) { - pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio); + if (info.getMaxAspectRatio() != 0) { + pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio()); } - if (info.minAspectRatio != 0) { - pw.println(prefix + "minAspectRatio=" + info.minAspectRatio); + if (info.getMinAspectRatio() != 0) { + pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio()); + } + if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) { + // Log the fact that we've overridden the min aspect ratio from the manifest + pw.println(prefix + "manifestMinAspectRatio=" + + info.getManifestMinAspectRatio()); } pw.println(prefix + "supportsSizeChanges=" + ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges())); @@ -1171,52 +1172,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A info.applicationInfo = aInfo; } - private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp); - } - - private boolean crossesVerticalSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mVerticalSizeConfigurations, firstDp, secondDp); - } - - private boolean crossesSmallestSizeThreshold(int firstDp, int secondDp) { - return crossesSizeThreshold(mSmallestSizeConfigurations, firstDp, secondDp); - } - - /** - * The purpose of this method is to decide whether the activity needs to be relaunched upon - * changing its size. In most cases the activities don't need to be relaunched, if the resize - * is small, all the activity content has to do is relayout itself within new bounds. There are - * cases however, where the activity's content would be completely changed in the new size and - * the full relaunch is required. - * - * The activity will report to us vertical and horizontal thresholds after which a relaunch is - * required. These thresholds are collected from the application resource qualifiers. For - * example, if application has layout-w600dp resource directory, then it needs a relaunch when - * we resize from width of 650dp to 550dp, as it crosses the 600dp threshold. However, if - * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side - * of the threshold. - */ - private static boolean crossesSizeThreshold(int[] thresholds, int firstDp, - int secondDp) { - if (thresholds == null) { - return false; - } - for (int i = thresholds.length - 1; i >= 0; i--) { - final int threshold = thresholds[i]; - if ((firstDp < threshold && secondDp >= threshold) - || (firstDp >= threshold && secondDp < threshold)) { - return true; - } - } - return false; - } - - void setSizeConfigurations(int[] horizontalSizeConfiguration, - int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) { - mHorizontalSizeConfigurations = horizontalSizeConfiguration; - mVerticalSizeConfigurations = verticalSizeConfigurations; - mSmallestSizeConfigurations = smallestSizeConfigurations; + void setSizeConfigurations(SizeConfigurationBuckets sizeConfigurations) { + mSizeConfigurations = sizeConfigurations; } private void scheduleActivityMovedToDisplay(int displayId, Configuration config) { @@ -7055,8 +7012,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in // order to use the extra available space. - final float maxAspectRatio = info.maxAspectRatio; - final float minAspectRatio = info.minAspectRatio; + final float maxAspectRatio = info.getMaxAspectRatio(); + final float minAspectRatio = info.getMinAspectRatio(); if (aspect > maxAspectRatio && maxAspectRatio != 0) { aspect = maxAspectRatio; } else if (aspect < minAspectRatio) { @@ -7293,21 +7250,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The rest of the condition is that only one side is smaller than the container, but it // still needs to exclude the cases where the size is limited by the fixed aspect ratio. - if (info.maxAspectRatio > 0) { + if (info.getMaxAspectRatio() > 0) { final float aspectRatio = (0.5f + Math.max(appWidth, appHeight)) / Math.min(appWidth, appHeight); - if (aspectRatio >= info.maxAspectRatio) { + if (aspectRatio >= info.getMaxAspectRatio()) { // The current size has reached the max aspect ratio. return false; } } - if (info.minAspectRatio > 0) { + if (info.getMinAspectRatio() > 0) { // The activity should have at least the min aspect ratio, so this checks if the // container still has available space to provide larger aspect ratio. final float containerAspectRatio = (0.5f + Math.max(containerAppWidth, containerAppHeight)) / Math.min(containerAppWidth, containerAppHeight); - if (containerAspectRatio <= info.minAspectRatio) { + if (containerAspectRatio <= info.getMinAspectRatio()) { // The long side has reached the parent. return false; } @@ -7474,9 +7431,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { - final float maxAspectRatio = info.maxAspectRatio; + final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); - final float minAspectRatio = info.minAspectRatio; + final float minAspectRatio = info.getMinAspectRatio(); if (task == null || rootTask == null || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) @@ -7792,26 +7749,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Determine what has changed. May be nothing, if this is a config that has come back from // the app after going idle. In that case we just want to leave the official config object // now in the activity and do nothing else. - final Configuration currentConfig = getConfiguration(); - int changes = lastReportedConfig.diff(currentConfig); - // We don't want to use size changes if they don't cross boundaries that are important to - // the app. - if ((changes & CONFIG_SCREEN_SIZE) != 0) { - final boolean crosses = crossesHorizontalSizeThreshold(lastReportedConfig.screenWidthDp, - currentConfig.screenWidthDp) - || crossesVerticalSizeThreshold(lastReportedConfig.screenHeightDp, - currentConfig.screenHeightDp); - if (!crosses) { - changes &= ~CONFIG_SCREEN_SIZE; - } - } - if ((changes & CONFIG_SMALLEST_SCREEN_SIZE) != 0) { - final int oldSmallest = lastReportedConfig.smallestScreenWidthDp; - final int newSmallest = currentConfig.smallestScreenWidthDp; - if (!crossesSmallestSizeThreshold(oldSmallest, newSmallest)) { - changes &= ~CONFIG_SMALLEST_SCREEN_SIZE; - } - } + int changes = lastReportedConfig.diff(getConfiguration()); + changes = SizeConfigurationBuckets.filterDiff( + changes, lastReportedConfig, getConfiguration(), mSizeConfigurations); // We don't want window configuration to cause relaunches. if ((changes & CONFIG_WINDOW_CONFIGURATION) != 0) { changes &= ~CONFIG_WINDOW_CONFIGURATION; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 9f6087834a4e..c78f9ec21516 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5661,16 +5661,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } return false; /* continue */ } - if (taskId != INVALID_TASK_ID) { + if (taskId == INVALID_TASK_ID) { + if (!nextWindow.canReceiveKeys()) { + return false; /* continue */ + } + } else { Task task = nextWindow.getTask(); if (task == null || !task.isTaskId(taskId)) { return false; /* continue */ } } - if (!nextWindow.canReceiveKeys()) { - return false; /* continue */ - } - return true; /* stop */ + + return true; /* stop, match found */ } }); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 73b05557565f..d2c9e29f9d37 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -231,7 +231,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.ISystemGestureExclusionListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; @@ -431,7 +431,7 @@ public class WindowManagerService extends IWindowManager.Stub * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY */ public static boolean sEnableRemoteKeyguardAnimation = - SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, true); + SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false); private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY = "ro.sf.disable_triple_buffer"; @@ -7145,10 +7145,10 @@ public class WindowManagerService extends IWindowManager.Stub * @param displayId the display for the request * @param behindClient token for a window, used to filter the search to windows behind it * @param taskId specifies the id of a task the result must belong to or -1 to match any task - * @param callbacks to receive responses + * @param listener to receive the response */ public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId, - IScrollCaptureCallbacks callbacks) { + IScrollCaptureResponseListener listener) { if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } @@ -7161,7 +7161,7 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.e(WM_ERROR, "Invalid displayId for requestScrollCapture: %d", displayId); responseBuilder.setDescription(String.format("bad displayId: %d", displayId)); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); return; } WindowState topWindow = null; @@ -7171,19 +7171,19 @@ public class WindowManagerService extends IWindowManager.Stub WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); if (targetWindow == null) { responseBuilder.setDescription("findScrollCaptureTargetWindow returned null"); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); return; } try { // Forward to the window for handling, which will respond using the callback. - targetWindow.mClient.requestScrollCapture(callbacks); + targetWindow.mClient.requestScrollCapture(listener); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); responseBuilder.setWindowTitle(targetWindow.getName()); responseBuilder.setDescription(String.format("caught exception: %s", e)); - callbacks.onScrollCaptureResponse(responseBuilder.build()); + listener.onScrollCaptureResponse(responseBuilder.build()); } } } catch (RemoteException e) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java index e0c5e328f8c7..8027e5b9d9bc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java @@ -79,16 +79,16 @@ public class CertificateMonitor { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException | IOException ce) { - Slog.e(LOG_TAG, ce, "Problem converting cert"); + Slog.e(LOG_TAG, "Problem converting cert", ce); return null; } try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { return keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { - Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): "); + Slog.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } catch (InterruptedException e1) { - Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): "); + Slog.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); } return null; @@ -100,9 +100,9 @@ public class CertificateMonitor { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { - Slog.e(LOG_TAG, e, "from CaCertUninstaller: "); + Slog.e(LOG_TAG, "from CaCertUninstaller: ", e); } catch (InterruptedException ie) { - Slog.w(LOG_TAG, ie, "CaCertUninstaller: "); + Slog.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ffae3abfbabb..70756787c561 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1056,7 +1056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { service.removeCredentialManagementApp(); } } catch (RemoteException | InterruptedException | IllegalStateException e) { - Log.e(LOG_TAG, "Unable to remove the credential management app"); + Slog.e(LOG_TAG, "Unable to remove the credential management app"); } }); } @@ -1149,18 +1149,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @OperationSafetyReason int reason) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); - Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)", + Slog.i(LOG_TAG, "setNextOperationSafety(%s, %s)", DevicePolicyManager.operationToString(operation), - DevicePolicyManager.operationSafetyReasonToString(reason))); + DevicePolicyManager.operationSafetyReasonToString(reason)); mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason); } @Override public boolean isSafeOperation(@OperationSafetyReason int reason) { if (VERBOSE_LOG) { - Slog.v(LOG_TAG, "checking isSafeOperation(" - + DevicePolicyManager.operationSafetyReasonToString(reason) - + ") using mSafetyChecker " + mSafetyChecker); + Slog.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s", + DevicePolicyManager.operationSafetyReasonToString(reason), mSafetyChecker); } return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason); } @@ -1893,9 +1892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } - Slog.i(LOG_TAG, String.format( - "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d", - doUserId, poUserId)); + Slog.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; " + + "profile: %d", doUserId, poUserId); Slog.i(LOG_TAG, "Giving the PO additional power..."); markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId); @@ -1940,11 +1938,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void uninstallOrDisablePackage(String packageName, int userHandle) { + private void uninstallOrDisablePackage(String packageName, @UserIdInt int userId) { final ApplicationInfo appInfo; try { appInfo = mIPackageManager.getApplicationInfo( - packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userHandle); + packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (RemoteException e) { // Shouldn't happen. return; @@ -1954,10 +1952,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - Slog.i(LOG_TAG, String.format( - "Package %s is pre-installed, marking disabled until used", packageName)); + Slog.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used", + packageName); mContext.getPackageManager().setApplicationEnabledSetting(packageName, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0 /* flags */); + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0); return; } @@ -1968,17 +1966,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int status = intent.getIntExtra( PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { - Slog.i(LOG_TAG, String.format( - "Package %s uninstalled for user %d", packageName, userHandle)); + Slog.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId); } else { - Slog.e(LOG_TAG, String.format( - "Failed to uninstall %s; status: %d", packageName, status)); + Slog.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status); } } }; - final PackageInstaller pi = mInjector.getPackageManager(userHandle).getPackageInstaller(); - pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender)); + final PackageInstaller pi = mInjector.getPackageManager(userId).getPackageInstaller(); + pi.uninstall(packageName, /* flags= */ 0, new IntentSender((IIntentSender) mLocalSender)); } @GuardedBy("getLockObject()") @@ -2177,7 +2173,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()); mOwners.writeDeviceOwner(); if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Device owner component filled in"); + Slog.v(LOG_TAG, "Device owner component filled in"); } } } @@ -2192,7 +2188,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // except for the "system controlled" ones. if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Migrating DO user restrictions"); + Slog.v(LOG_TAG, "Migrating DO user restrictions"); } migrated = true; @@ -2220,7 +2216,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = ui.id; if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Migrating PO user restrictions for user " + userId); + Slog.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId); } migrated = true; @@ -2243,7 +2239,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } if (VERBOSE_LOG && migrated) { - Log.v(LOG_TAG, "User restrictions migrated."); + Slog.v(LOG_TAG, "User restrictions migrated."); } } @@ -2271,9 +2267,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (VERBOSE_LOG) { - Log.v(LOG_TAG, "origRestrictions=" + origRestrictions); - Log.v(LOG_TAG, "newBaseRestrictions=" + newBaseRestrictions); - Log.v(LOG_TAG, "newOwnerRestrictions=" + newOwnerRestrictions); + Slog.v(LOG_TAG, "origRestrictions=%s", origRestrictions); + Slog.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions); + Slog.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions); } mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(), newBaseRestrictions); @@ -2768,9 +2764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private JournaledFile makeJournaledFile(@UserIdInt int userId, String fileName) { final String base = new File(getPolicyFileDirectory(userId), fileName) .getAbsolutePath(); - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Opening " + base); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Opening %s", base); return new JournaledFile(new File(base), new File(base + ".tmp")); } @@ -4920,8 +4914,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (!validationErrors.isEmpty()) { - Log.w(LOG_TAG, "Failed to reset password due to constraint violation: " - + validationErrors.get(0)); + Slog.w(LOG_TAG, "Failed to reset password due to constraint violation: %s", + validationErrors.get(0)); return false; } } @@ -5349,7 +5343,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); if (alias == null) { - Log.w(LOG_TAG, "Problem installing cert"); + Slog.w(LOG_TAG, "Problem installing cert"); return false; } @@ -5422,12 +5416,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return true; } catch (RemoteException e) { - Log.e(LOG_TAG, "Installing certificate", e); + Slog.e(LOG_TAG, "Installing certificate", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while installing certificate", e); + Slog.w(LOG_TAG, "Interrupted while installing certificate", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5472,12 +5466,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return keyChain.removeKeyPair(alias); } catch (RemoteException e) { - Log.e(LOG_TAG, "Removing keypair", e); + Slog.e(LOG_TAG, "Removing keypair", e); } finally { keyChainConnection.close(); } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while removing keypair", e); + Slog.w(LOG_TAG, "Interrupted while removing keypair", e); Thread.currentThread().interrupt(); } finally { Binder.restoreCallingIdentity(id); @@ -5495,9 +5489,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { KeyChain.bindAsUser(mContext, caller.getUserHandle())) { return keyChainConnection.getService().containsKeyPair(alias); } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying keypair", e); + Slog.e(LOG_TAG, "Querying keypair", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while querying keypair", e); + Slog.w(LOG_TAG, "Interrupted while querying keypair", e); Thread.currentThread().interrupt(); } return false; @@ -5539,7 +5533,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return false; } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying grant to wifi auth. ", e); + Slog.e(LOG_TAG, "Querying grant to wifi auth.", e); return false; } }); @@ -5580,11 +5574,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { keyChain.setGrant(granteeUid, alias, hasGrant); return true; } catch (RemoteException e) { - Log.e(LOG_TAG, "Setting grant for package.", e); + Slog.e(LOG_TAG, "Setting grant for package.", e); return false; } } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while setting key grant", e); + Slog.w(LOG_TAG, "Interrupted while setting key grant", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5621,9 +5615,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return result; } catch (RemoteException e) { - Log.e(LOG_TAG, "Querying keypair grants", e); + Slog.e(LOG_TAG, "Querying keypair grants", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while querying keypair grants", e); + Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e); Thread.currentThread().interrupt(); } return Collections.emptyList(); @@ -5748,7 +5742,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { - Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair."); + Slog.e(LOG_TAG, "Only the caller can be granted access to the generated keypair."); logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; } @@ -5774,8 +5768,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int generationResult = keyChain.generateKeyPair(algorithm, new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { - Log.e(LOG_TAG, String.format( - "KeyChain failed to generate a keypair, error %d.", generationResult)); + Slog.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.", + generationResult); logGenerateKeyPairFailure(caller, isCredentialManagementApp); switch (generationResult) { case KeyChain.KEY_GEN_STRONGBOX_UNAVAILABLE: @@ -5814,7 +5808,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); } catch (CertificateException e) { logGenerateKeyPairFailure(caller, isCredentialManagementApp); - Log.e(LOG_TAG, "While retrieving certificate chain.", e); + Slog.e(LOG_TAG, "While retrieving certificate chain.", e); return false; } @@ -5829,9 +5823,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } } catch (RemoteException e) { - Log.e(LOG_TAG, "KeyChain error while generating a keypair", e); + Slog.e(LOG_TAG, "KeyChain error while generating a keypair", e); } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while generating keypair", e); + Slog.w(LOG_TAG, "Interrupted while generating keypair", e); Thread.currentThread().interrupt(); } finally { mInjector.binderRestoreCallingIdentity(id); @@ -5889,10 +5883,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); return true; } catch (InterruptedException e) { - Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e); + Slog.w(LOG_TAG, "Interrupted while setting keypair certificate", e); Thread.currentThread().interrupt(); } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed setting keypair certificate", e); + Slog.e(LOG_TAG, "Failed setting keypair certificate", e); } finally { mInjector.binderRestoreCallingIdentity(id); } @@ -5966,7 +5960,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } catch (Exception e) { // Caller could throw RuntimeException or RemoteException back across processes. Catch // everything just to be sure. - Log.e(LOG_TAG, "error while responding to callback", e); + Slog.e(LOG_TAG, "error while responding to callback", e); } } @@ -6323,7 +6317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean isUserSelectable) { // Should not be user selectable if (isUserSelectable) { - Log.e(LOG_TAG, "The credential management app is not allowed to install a " + Slog.e(LOG_TAG, "The credential management app is not allowed to install a " + "user selectable key pair"); return false; } @@ -6523,8 +6517,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Persist the request so the device is automatically factory-reset on next start if // the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls // its callback. - Slog.i(LOG_TAG, String.format("Persisting factory reset request as it could be " - + "delayed by %s", mSafetyChecker)); + Slog.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s", + mSafetyChecker); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); policy.setDelayedFactoryReset(reason, wipeExtRequested, wipeEuicc, @@ -6638,8 +6632,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { int userId = admin != null ? admin.getUserHandle().getIdentifier() : caller.getUserId(); - Slog.i(LOG_TAG, String.format("wipeDataWithReason(%s): admin=%s, user=%d", - wipeReasonForUser, admin, userId)); + Slog.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin, + userId); if (calledByProfileOwnerOnOrgOwnedDevice) { // When wipeData is called on the parent instance, it implies wiping the entire device. if (calledOnParentInstance) { @@ -7439,7 +7433,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle); } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify WindowManager.", e); + Slog.w(LOG_TAG, "Unable to notify WindowManager.", e); } }); } @@ -8906,19 +8900,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // thrown but null data can be returned; if the appInfo for the specified package cannot // be found then return false to prevent crashing the app. if (appInfo == null) { - Log.w(LOG_TAG, - String.format("appInfo could not be found for package %s", packageName)); + Slog.w(LOG_TAG, "appInfo could not be found for package %s", packageName); return false; } else if (uid != appInfo.uid) { String message = String.format("Package %s (uid=%d) does not match provided uid %d", packageName, appInfo.uid, uid); - Log.w(LOG_TAG, message); + Slog.w(LOG_TAG, message); throw new SecurityException(message); } } catch (RemoteException e) { // If an exception is caught obtaining the appInfo just return false to prevent crashing // apps due to an internal error. - Log.e(LOG_TAG, "Exception caught obtaining appInfo for package " + packageName, e); + Slog.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName); return false; } return true; @@ -8934,7 +8927,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String message = String.format( "Calling uid %d, pid %d cannot check device identifier access for package %s " + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid); - Log.w(LOG_TAG, message); + Slog.w(LOG_TAG, message); throw new SecurityException(message); } } @@ -8942,14 +8935,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Canonical name for a given package. */ - private String getApplicationLabel(String packageName, int userHandle) { + private String getApplicationLabel(String packageName, @UserIdInt int userId) { return mInjector.binderWithCleanCallingIdentity(() -> { final Context userContext; try { - UserHandle handle = new UserHandle(userHandle); - userContext = mContext.createPackageContextAsUser(packageName, 0, handle); + UserHandle userHandle = UserHandle.of(userId); + userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0, + userHandle); } catch (PackageManager.NameNotFoundException nnfe) { - Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe); + Slog.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId); return null; } ApplicationInfo appInfo = userContext.getApplicationInfo(); @@ -9568,9 +9562,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } result.add(info.options); } else { - Log.w(LOG_TAG, "Ignoring admin " + active.info - + " because it has trust options but doesn't declare " - + "KEYGUARD_DISABLE_TRUST_AGENTS"); + Slog.w(LOG_TAG, "Ignoring admin %s because it has trust options but doesn't" + + " declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info); } } else if (disablesTrust) { allAdminsHaveOptions = false; @@ -9708,7 +9701,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { userIdToCheck); systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } catch (RemoteException e) { - Log.i(LOG_TAG, "Can't talk to package managed", e); + Slog.i(LOG_TAG, "Can't talk to package managed", e); } if (!systemService && !permittedList.contains(enabledPackage)) { return false; @@ -10189,7 +10182,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { user = userInfo.getUserHandle(); } } catch (UserManager.CheckedUserOperationException e) { - Log.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e); + Slog.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e); } } finally { mInjector.binderRestoreCallingIdentity(id); @@ -10260,8 +10253,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } catch (RemoteException e) { // Does not happen, same process - Slog.wtf(LOG_TAG, String.format("Failed to install admin package %s for user %d", - adminPkg, userId), e); + Slog.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d", + adminPkg, userId); } // Set admin. @@ -10310,7 +10303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, /* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true); } else { - Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); + Slog.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId); setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); } } @@ -10366,8 +10359,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; if (isAdminAffectedByRestriction(who, restriction, caller.getUserId())) { - Log.w(LOG_TAG, "The device owner cannot remove a user because " - + restriction + " is enabled, and was not set by the device owner"); + Slog.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and " + + "was not set by the device owner", restriction); return false; } return mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle.getIdentifier()); @@ -10404,7 +10397,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return mInjector.getIActivityManager().switchUser(userId); } catch (RemoteException e) { - Log.e(LOG_TAG, "Couldn't switch user", e); + Slog.e(LOG_TAG, "Couldn't switch user", e); return false; } finally { mInjector.binderRestoreCallingIdentity(id); @@ -10422,19 +10415,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { - Log.w(LOG_TAG, "Managed profile cannot be started in background"); + Slog.w(LOG_TAG, "Managed profile cannot be started in background"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } final long id = mInjector.binderClearCallingIdentity(); try { if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) { - Log.w(LOG_TAG, "Cannot start user " + userId + ", too many users in background"); + Slog.w(LOG_TAG, "Cannot start user %d, too many users in background", userId); return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS; } if (mInjector.getIActivityManager().startUserInBackground(userId)) { - Log.i(LOG_TAG, "Started used " + userId + " in background"); + Slog.i(LOG_TAG, "Started used %d in background", userId); return UserManager.USER_OPERATION_SUCCESS; } else { return UserManager.USER_OPERATION_ERROR_UNKNOWN; @@ -10457,7 +10450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { - Log.w(LOG_TAG, "Managed profile cannot be stopped"); + Slog.w(LOG_TAG, "Managed profile cannot be stopped"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } @@ -10480,14 +10473,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isManagedProfile(callingUserId)) { - Log.w(LOG_TAG, "Managed profile cannot be logout"); + Slog.w(LOG_TAG, "Managed profile cannot be logout"); return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE; } final long id = mInjector.binderClearCallingIdentity(); try { if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) { - Log.w(LOG_TAG, "Failed to switch to primary user"); + Slog.w(LOG_TAG, "Failed to switch to primary user"); // This should never happen as target user is UserHandle.USER_SYSTEM return UserManager.USER_OPERATION_ERROR_UNKNOWN; } @@ -11264,8 +11257,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isCrossProfileQuickContactDisabled(managedUserId)) { if (VERBOSE_LOG) { - Log.v(LOG_TAG, - "Cross-profile contacts access disabled for user " + managedUserId); + Slog.v(LOG_TAG, "Cross-profile contacts access disabled for user %d", + managedUserId); } return; } @@ -11278,7 +11271,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * @return true if cross-profile QuickContact is disabled */ - private boolean isCrossProfileQuickContactDisabled(int userId) { + private boolean isCrossProfileQuickContactDisabled(@UserIdInt int userId) { return getCrossProfileCallerIdDisabledForUser(userId) && getCrossProfileContactsSearchDisabledForUser(userId); } @@ -11287,23 +11280,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * @return the user ID of the managed user that is linked to the current user, if any. * Otherwise -1. */ - public int getManagedUserId(int callingUserId) { - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId); - } + public int getManagedUserId(@UserIdInt int callingUserId) { + if (VERBOSE_LOG) Slog.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId); for (UserInfo ui : mUserManager.getProfiles(callingUserId)) { if (ui.id == callingUserId || !ui.isManagedProfile()) { continue; // Caller user self, or not a managed profile. Skip. } - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Managed user=" + ui.id); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user=%d", ui.id); return ui.id; } - if (VERBOSE_LOG) { - Log.v(LOG_TAG, "Managed user not found."); - } + if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user not found."); return -1; } @@ -11602,7 +11589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Some settings are no supported any more. However we do not want to throw a // SecurityException to avoid breaking apps. if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) { - Log.i(LOG_TAG, "Global setting no longer supported: " + setting); + Slog.i(LOG_TAG, "Global setting no longer supported: %s", setting); return; } @@ -12338,7 +12325,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING, UserHandle.of(userId))) { - Log.e(LOG_TAG, "printing is enabled"); + Slog.e(LOG_TAG, "printing is enabled for user %d", userId); return null; } String ownerPackage = mOwners.getProfileOwnerPackage(userId); @@ -12351,22 +12338,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { return pm.getPackageInfo(packageName, 0); } catch (NameNotFoundException e) { - Log.e(LOG_TAG, "getPackageInfo error", e); + Slog.e(LOG_TAG, "getPackageInfo error", e); return null; } }); if (packageInfo == null) { - Log.e(LOG_TAG, "packageInfo is inexplicably null"); + Slog.e(LOG_TAG, "packageInfo is inexplicably null"); return null; } ApplicationInfo appInfo = packageInfo.applicationInfo; if (appInfo == null) { - Log.e(LOG_TAG, "appInfo is inexplicably null"); + Slog.e(LOG_TAG, "appInfo is inexplicably null"); return null; } CharSequence appLabel = pm.getApplicationLabel(appInfo); if (appLabel == null) { - Log.e(LOG_TAG, "appLabel is inexplicably null"); + Slog.e(LOG_TAG, "appLabel is inexplicably null"); return null; } return ((Context) ActivityThread.currentActivityThread().getSystemUiContext()) @@ -12420,9 +12407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(intent); Objects.requireNonNull(parentHandle); final int userId = parentHandle.getIdentifier(); - Slog.i(LOG_TAG, - String.format("Sending %s broadcast to manifest receivers.", - intent.getAction())); + Slog.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction()); try { final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers( intent, /* resolvedType= */ null, @@ -12432,9 +12417,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (checkCrossProfilePackagePermissions(packageName, userId, requiresPermission) || checkModifyQuietModePermission(packageName, userId)) { - Slog.i(LOG_TAG, - String.format("Sending %s broadcast to %s.", intent.getAction(), - packageName)); + Slog.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(), + packageName); final Intent packageIntent = new Intent(intent) .setComponent(receiver.getComponentInfo().getComponentName()) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); @@ -12442,9 +12426,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } } catch (RemoteException ex) { - Slog.w(LOG_TAG, - String.format("Cannot get list of broadcast receivers for %s because: %s.", - intent.getAction(), ex)); + Slog.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.", + intent.getAction(), ex); } } @@ -12462,9 +12445,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */ -1, /* exported= */ true); } catch (NameNotFoundException ex) { - Slog.w(LOG_TAG, - String.format("Cannot find the package %s to check for permissions.", - packageName)); + Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.", + packageName); return false; } } @@ -12493,9 +12475,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission( packageName, userId); } catch (NameNotFoundException ex) { - Slog.w(LOG_TAG, - String.format("Cannot find the package %s to check for permissions.", - packageName)); + Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.", + packageName); return false; } } @@ -12569,8 +12550,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // TODO(b/178494483): use EventLog instead // TODO(b/178494483): log metrics? if (VERBOSE_LOG) { - Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b", - DevicePolicyManager.operationSafetyReasonToString(reason), isSafe)); + Slog.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b", + DevicePolicyManager.operationSafetyReasonToString(reason), isSafe); } Preconditions.checkArgument(mSafetyChecker == checker, "invalid checker: should be %s, was %s", mSafetyChecker, checker); @@ -12852,7 +12833,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { runningUserIds = mInjector.getIActivityManager().getRunningUserIds(); } catch (RemoteException e) { // Shouldn't happen. - Log.e(LOG_TAG, "Could not retrieve the list of running users", e); + Slog.e(LOG_TAG, "Could not retrieve the list of running users", e); return; } // Send broadcasts to corresponding profile owners if any. @@ -13213,9 +13194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode() ? UserHandle.USER_SYSTEM : callingUserId; - Slog.i(LOG_TAG, - String.format("Calling user %d, device owner will be set on user %d", - callingUserId, deviceOwnerUserId)); + Slog.i(LOG_TAG, "Calling user %d, device owner will be set on user %d", + callingUserId, deviceOwnerUserId); // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb. return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null, deviceOwnerUserId, callingUserId, /* isAdb= */ false, @@ -13245,9 +13225,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle); if (mUserManager.getUserInfo(callingUserId).isProfile()) { - Slog.i(LOG_TAG, - String.format("Calling user %d is a profile, cannot add another.", - callingUserId)); + Slog.i(LOG_TAG, "Calling user %d is a profile, cannot add another.", callingUserId); // The check is called from inside a managed profile. A managed profile cannot // be provisioned from within another managed profile. return CODE_CANNOT_ADD_MANAGED_PROFILE; @@ -13260,16 +13238,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Do not allow adding a managed profile if there's a restriction. if (addingProfileRestricted) { - Slog.i(LOG_TAG, String.format( - "Adding a profile is restricted: User %s Has device owner? %b", - callingUserHandle, hasDeviceOwner)); + Slog.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b", + callingUserHandle, hasDeviceOwner); return CODE_CANNOT_ADD_MANAGED_PROFILE; } // Bail out if we are trying to provision a work profile but one already exists. if (!mUserManager.canAddMoreManagedProfiles( callingUserId, /* allowedToRemoveOne= */ false)) { - Slog.i(LOG_TAG, String.format("A work profile already exists.")); + Slog.i(LOG_TAG, "A work profile already exists."); return CODE_CANNOT_ADD_MANAGED_PROFILE; } } finally { @@ -13757,9 +13734,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { who.flattenToString(), userId)); } - Slog.i(LOG_TAG, String.format( - "Marking %s as profile owner on organization-owned device for user %d", - who.flattenToString(), userId)); + Slog.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d", + who.flattenToString(), userId); // First, set restriction on removing the profile. mInjector.binderWithCleanCallingIdentity(() -> { @@ -14172,7 +14148,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { // force stop the package before uninstalling mInjector.getIActivityManager().forceStopPackage(packageName, userId); } catch (RemoteException re) { - Log.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package"); + Slog.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package"); } final Uri packageURI = Uri.parse("package:" + packageName); final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); @@ -14428,7 +14404,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (getLockObject()) { if (owner == null || !isAdminTestOnlyLocked(owner, userId)) { - Log.w(LOG_TAG, + Slog.w(LOG_TAG, "Non test-only owner can't be installed with existing accounts."); return true; } @@ -14442,20 +14418,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean compatible = true; for (Account account : accounts) { if (hasAccountFeatures(am, account, feature_disallow)) { - Log.e(LOG_TAG, account + " has " + feature_disallow[0]); + Slog.e(LOG_TAG, "%s has %s", account, feature_disallow[0]); compatible = false; break; } if (!hasAccountFeatures(am, account, feature_allow)) { - Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]); + Slog.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]); compatible = false; break; } } if (compatible) { - Log.w(LOG_TAG, "All accounts are compatible"); + Slog.w(LOG_TAG, "All accounts are compatible"); } else { - Log.e(LOG_TAG, "Found incompatible accounts"); + Slog.e(LOG_TAG, "Found incompatible accounts"); } return !compatible; }); @@ -14465,7 +14441,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { return am.hasFeatures(account, features, null, null).getResult(); } catch (Exception e) { - Log.w(LOG_TAG, "Failed to get account feature", e); + Slog.w(LOG_TAG, "Failed to get account feature", e); return false; } } @@ -14765,8 +14741,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 0, // flags targetUserId); if (info == null || info.serviceInfo == null) { - Log.e(LOG_TAG, "Fail to look up the service: " + rawIntent - + " or user " + targetUserId + " is not running"); + Slog.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent, + targetUserId); return null; } if (!expectedPackageName.equals(info.serviceInfo.packageName)) { @@ -15260,7 +15236,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting)); } else { - Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to add override apn"); return Telephony.Carriers.INVALID_APN_ID; } } @@ -15284,7 +15260,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting)); } else { - Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn"); return false; } } @@ -15327,7 +15303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity( () -> tm.getDevicePolicyOverrideApns(mContext)); } - Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns"); + Slog.w(LOG_TAG, "TelephonyManager is null when trying to get override apns"); return Collections.emptyList(); } @@ -15480,8 +15456,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); - final String currentMode = - ConnectivityManager.getPrivateDnsMode(mContext.getContentResolver()); + final String currentMode = ConnectivityManager.getPrivateDnsMode(mContext); switch (currentMode) { case ConnectivityManager.PRIVATE_DNS_MODE_OFF: return PRIVATE_DNS_MODE_OFF; @@ -15828,8 +15803,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) { - Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile" - + "calendar APIs", packageName)); + Slog.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs", + packageName); return false; } final Intent intent = new Intent( @@ -15843,7 +15818,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId)); } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, "View event activity not found", e); + Slog.e(LOG_TAG, "View event activity not found", e); return false; } return true; @@ -15857,7 +15832,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { packageName, UserHandle.getUserId(callingUid)); return packageUid == callingUid; } catch (NameNotFoundException e) { - Log.d(LOG_TAG, "Calling package not found", e); + Slog.d(LOG_TAG, "Calling package not found", e); return false; } }); @@ -15967,8 +15942,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long deadline = admin.mProfileOffDeadline; final int result = makeSuspensionReasons(admin.mSuspendPersonalApps, deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline); - Slog.d(LOG_TAG, String.format("getPersonalAppsSuspendedReasons user: %d; result: %d", - mInjector.userHandleGetCallingUserId(), result)); + Slog.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d", + mInjector.userHandleGetCallingUserId(), result); return result; } } @@ -16052,9 +16027,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked); suspendedExplicitly = profileOwner.mSuspendPersonalApps; suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED; - Slog.d(LOG_TAG, String.format( - "Personal apps suspended explicitly: %b, deadline state: %d", - suspendedExplicitly, deadlineState)); + Slog.d(LOG_TAG, "Personal apps suspended explicitly: %b, deadline state: %d", + suspendedExplicitly, deadlineState); final int notificationState = unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState; updateProfileOffDeadlineNotificationLocked( @@ -16155,8 +16129,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (getUserData(userId).mAppsSuspended == suspended) { return; } - Slog.i(LOG_TAG, String.format("%s personal apps for user %d", - suspended ? "Suspending" : "Unsuspending", userId)); + Slog.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", + userId); if (suspended) { suspendPersonalAppsInPackageManager(userId); @@ -16376,8 +16350,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(!TextUtils.isEmpty(organizationId), "Enterprise ID may not be empty."); - Log.i(LOG_TAG, - String.format("Setting Enterprise ID to %s for user %d", organizationId, userId)); + Slog.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId); final String ownerPackage; synchronized (getLockObject()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java index 2959c10d5508..fa6ef006c61d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java @@ -207,7 +207,7 @@ public class RemoteBugreportManager { return true; } catch (RemoteException re) { // should never happen - Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service"); + Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re); return false; } finally { mInjector.binderRestoreCallingIdentity(callingIdentity); diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java index 87b2c84a30f7..4c5bbebdfd45 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java @@ -48,6 +48,8 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.IOException; import java.io.OutputStream; import java.util.Objects; +import java.util.concurrent.CompletableFuture; + /** * Handles per-user requests received by @@ -60,6 +62,11 @@ public final class MusicRecognitionManagerPerUserService extends implements RemoteMusicRecognitionService.Callbacks { private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName(); + private static final String MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG = + "MusicRecognitionManagerService"; + private static final String KEY_MUSIC_RECOGNITION_SERVICE_ATTRIBUTION_TAG = + "android.media.musicrecognition.attributiontag"; + // Number of bytes per sample of audio (which is a short). private static final int BYTES_PER_SAMPLE = 2; private static final int MAX_STREAMING_SECONDS = 24; @@ -68,18 +75,24 @@ public final class MusicRecognitionManagerPerUserService extends @GuardedBy("mLock") private RemoteMusicRecognitionService mRemoteService; private final AppOpsManager mAppOpsManager; + private final String mAttributionMessage; - private String mAttributionTag; - private String mAttributionMessage; + // Service info of the remote MusicRecognitionService (which the audio gets forwarded to). private ServiceInfo mServiceInfo; + private CompletableFuture<String> mAttributionTagFuture; MusicRecognitionManagerPerUserService( @NonNull MusicRecognitionManagerService primary, @NonNull Object lock, int userId) { super(primary, lock, userId); - mAppOpsManager = getContext().getSystemService(AppOpsManager.class); + + // When attributing audio-access, this establishes that audio access is performed by + // MusicRecognitionManager (on behalf of the receiving service, whose attribution tag, + // provided by mAttributionTagFuture, is used for the actual calls to startProxyOp(...). + mAppOpsManager = getContext().createAttributionContext( + MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG).getSystemService(AppOpsManager.class); mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId); - mAttributionTag = null; + mAttributionTagFuture = null; mServiceInfo = null; } @@ -126,10 +139,13 @@ public final class MusicRecognitionManagerPerUserService extends new MusicRecognitionServiceCallback(clientCallback), mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + try { mServiceInfo = getContext().getPackageManager().getServiceInfo( - mRemoteService.getComponentName(), 0); + mRemoteService.getComponentName(), PackageManager.GET_META_DATA); + mAttributionTagFuture = mRemoteService.getAttributionTag(); + Slog.i(TAG, "Remote service bound: " + mRemoteService.getComponentName()); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Service was not found.", e); } @@ -172,11 +188,13 @@ public final class MusicRecognitionManagerPerUserService extends ParcelFileDescriptor audioSink = clientPipe.second; ParcelFileDescriptor clientRead = clientPipe.first; - mMaster.mExecutorService.execute(() -> { - streamAudio(recognitionRequest, clientCallback, audioSink); - }); + mAttributionTagFuture.thenAcceptAsync( + tag -> { + streamAudio(tag, recognitionRequest, clientCallback, audioSink); + }, mMaster.mExecutorService); + // Send the pipe down to the lookup service while we write to it asynchronously. - mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat()); + mRemoteService.onAudioStreamStarted(clientRead, recognitionRequest.getAudioFormat()); } /** @@ -186,10 +204,12 @@ public final class MusicRecognitionManagerPerUserService extends * @param clientCallback the callback to notify on errors. * @param audioSink the sink to which to stream audio to. */ - private void streamAudio(@NonNull RecognitionRequest recognitionRequest, - IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) { + private void streamAudio(@Nullable String attributionTag, + @NonNull RecognitionRequest recognitionRequest, + IMusicRecognitionManagerCallback clientCallback, + ParcelFileDescriptor audioSink) { try { - startRecordAudioOp(); + startRecordAudioOp(attributionTag); } catch (SecurityException e) { // A security exception can occur if the MusicRecognitionService (receiving the audio) // does not (or does no longer) hold the necessary permissions to record audio. @@ -214,7 +234,7 @@ public final class MusicRecognitionManagerPerUserService extends Slog.e(TAG, "Audio streaming stopped.", e); } finally { audioRecord.release(); - finishRecordAudioOp(); + finishRecordAudioOp(attributionTag); try { clientCallback.onAudioStreamClosed(); } catch (RemoteException ignored) { @@ -323,23 +343,32 @@ public final class MusicRecognitionManagerPerUserService extends * Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the * audio). */ - private void startRecordAudioOp() { - mAppOpsManager.startProxyOp( + private void startRecordAudioOp(@Nullable String attributionTag) { + int status = mAppOpsManager.startProxyOp( Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), mServiceInfo.applicationInfo.uid, mServiceInfo.packageName, - mAttributionTag, + attributionTag, mAttributionMessage); + // The above should already throw a SecurityException. This is just a fallback. + if (status != AppOpsManager.MODE_ALLOWED) { + throw new SecurityException(String.format( + "Failed to obtain RECORD_AUDIO permission (status: %d) for " + + "receiving service: %s", status, mServiceInfo.getComponentName())); + } + Slog.i(TAG, String.format( + "Starting audio streaming. Attributing to %s (%d) with tag '%s'", + mServiceInfo.packageName, mServiceInfo.applicationInfo.uid, attributionTag)); } /** Tracks that the RECORD_AUDIO operation finished. */ - private void finishRecordAudioOp() { + private void finishRecordAudioOp(@Nullable String attributionTag) { mAppOpsManager.finishProxyOp( Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), mServiceInfo.applicationInfo.uid, mServiceInfo.packageName, - mAttributionTag); + attributionTag); } /** Establishes an audio stream from the DSP audio source. */ diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java index 6c7d673ffe11..99b448211492 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java @@ -20,15 +20,20 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.media.AudioFormat; +import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback; import android.media.musicrecognition.IMusicRecognitionService; import android.media.musicrecognition.MusicRecognitionService; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.text.format.DateUtils; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback; +import java.util.concurrent.CompletableFuture; + + /** Remote connection to an instance of {@link MusicRecognitionService}. */ public class RemoteMusicRecognitionService extends AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService, @@ -81,9 +86,26 @@ public class RemoteMusicRecognitionService extends * Sends the given descriptor to the app's {@link MusicRecognitionService} to read the * audio. */ - public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd, + public void onAudioStreamStarted(@NonNull ParcelFileDescriptor fd, @NonNull AudioFormat audioFormat) { scheduleAsyncRequest( binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback)); } + + + /** + * Returns the name of the <attribution> tag defined in the remote service's manifest. + */ + public CompletableFuture<String> getAttributionTag() { + CompletableFuture<String> attributionTagFuture = new CompletableFuture<String>(); + scheduleAsyncRequest( + binder -> binder.getAttributionTag( + new IMusicRecognitionAttributionTagCallback.Stub() { + @Override + public void onAttributionTag(String tag) throws RemoteException { + attributionTagFuture.complete(tag); + } + })); + return attributionTagFuture; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f5d831bbe73e..67fe7bf436b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -117,6 +117,7 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import android.view.View; import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -1699,6 +1700,20 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testFindScrollCaptureTargetWindow_cantReceiveKeys() { + DisplayContent display = createNewDisplay(); + Task stack = createTaskStackOnDisplay(display); + Task task = createTaskInStack(stack, 0 /* userId */); + WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); + WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible"); + invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false + + WindowState result = display.findScrollCaptureTargetWindow(null, + ActivityTaskManager.INVALID_TASK_ID); + assertEquals(activityWindow, result); + } + + @Test public void testFindScrollCaptureTargetWindow_taskId() { DisplayContent display = createNewDisplay(); Task stack = createTaskStackOnDisplay(display); @@ -1711,6 +1726,19 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() { + DisplayContent display = createNewDisplay(); + Task stack = createTaskStackOnDisplay(display); + Task task = createTaskInStack(stack, 0 /* userId */); + WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); + window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false + WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); + + WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId); + assertEquals(window, result); + } + + @Test public void testEnsureActivitiesVisibleNotRecursive() { final TaskDisplayArea mockTda = mock(TaskDisplayArea.class); final boolean[] called = { false }; 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 36cf9c9fb0cf..5c7e58036aba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -161,7 +161,8 @@ public class SizeCompatTests extends WindowTestsBase { final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds(); final float aspectRatio = 1.2f; - mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio; + mActivity.info.setMaxAspectRatio(aspectRatio); + mActivity.info.setMinAspectRatio(aspectRatio); prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED); final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); @@ -780,6 +781,139 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioMedium() { + setUpDisplaySizeWithApp(1000, 1200); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 3:2 aspect ratio + assertEquals(1200, activity.getBounds().height()); + assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioLowerThanManifest() { + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(2f) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect, because the manifest aspect ratio is + // larger (2:1) + assertEquals(1600, activity.getBounds().height()); + assertEquals(800, activity.getBounds().width()); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatioLargerThanManifest() { + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setMinAspectRatio(1.1f) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect, because the manifest aspect ratio is + // larger (2:1) + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatioLarge() { + setUpDisplaySizeWithApp(1500, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 16:9 aspect ratio + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatio_Both() { + // If multiple override aspect ratios are set, we should use the largest one + + setUpDisplaySizeWithApp(1400, 1600); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override forces the activity into a 16:9 aspect ratio + assertEquals(1600, activity.getBounds().height()); + assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM}) + public void testOverrideMinAspectRatioWithoutGlobalOverride() { + // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without + // OVERRIDE_MIN_ASPECT_RATIO being also set. + + setUpDisplaySizeWithApp(1000, 1200); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + // The per-package override should have no effect + assertEquals(1200, activity.getBounds().height()); + assertEquals(1000, activity.getBounds().width()); + } + + @Test public void testLaunchWithFixedRotationTransform() { final int dw = 1000; final int dh = 2500; @@ -872,7 +1006,7 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed // orientation letterbox. mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f); - mActivity.info.minAspectRatio = 3; + mActivity.info.setMinAspectRatio(3); prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT); final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); @@ -889,7 +1023,8 @@ public class SizeCompatTests extends WindowTestsBase { // Activity bounds should respect minimum aspect ratio for activity. assertEquals(displayBounds.height(), activityBounds.height()); - assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio), + assertEquals((int) Math.rint(displayBounds.height() + / mActivity.info.getManifestMinAspectRatio()), activityBounds.width()); } @@ -918,7 +1053,8 @@ public class SizeCompatTests extends WindowTestsBase { // Activity bounds should respect maximum aspect ratio for activity. assertEquals(displayBounds.height(), activityBounds.height()); - assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio), + assertEquals((int) Math.rint(displayBounds.height() + / mActivity.info.getMaxAspectRatio()), activityBounds.width()); } @@ -1098,7 +1234,8 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(newActivity.inSizeCompatMode()); assertEquals(displayBounds.height(), newActivityBounds.height()); - assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio), + assertEquals((long) Math.rint(newActivityBounds.height() + / newActivity.info.getMaxAspectRatio()), newActivityBounds.width()); } @@ -1347,7 +1484,7 @@ public class SizeCompatTests extends WindowTestsBase { : RESIZE_MODE_RESIZEABLE; activity.mVisibleRequested = true; if (maxAspect >= 0) { - activity.info.maxAspectRatio = maxAspect; + activity.info.setMaxAspectRatio(maxAspect); } if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) { activity.info.screenOrientation = screenOrientation; diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index cac69657f5cc..21536a6e1cfb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -136,7 +136,10 @@ class TestDisplayContent extends DisplayContent { final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); final TestDisplayContent newDisplay = createInternal(display); - + // Ensure letterbox aspect ratio is not overridden on any device target. + // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by + // the below method, is set on some device form factors. + mService.mWindowManager.setFixedOrientationLetterboxAspectRatio(0); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); spyOn(displayPolicy); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index 001afe3e2637..b3a07454c1a0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -22,10 +22,11 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureResponseListener; import android.view.IWindow; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ScrollCaptureResponse; import android.window.ClientWindowFrames; import com.android.internal.os.IResultReceiver; @@ -116,7 +117,15 @@ public class TestIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException { + public void requestScrollCapture(IScrollCaptureResponseListener listener) + throws RemoteException { + try { + listener.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); + + } catch (RemoteException ex) { + // ignore + } } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 4a7784cf1b36..779457bb3e2f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -714,6 +714,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private int mLaunchMode; private int mResizeMode = RESIZE_MODE_RESIZEABLE; private float mMaxAspectRatio; + private float mMinAspectRatio; private boolean mSupportsSizeChanges; private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; private boolean mLaunchTaskBehind = false; @@ -793,6 +794,11 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + ActivityBuilder setMinAspectRatio(float minAspectRatio) { + mMinAspectRatio = minAspectRatio; + return this; + } + ActivityBuilder setSupportsSizeChanges(boolean supportsSizeChanges) { mSupportsSizeChanges = supportsSizeChanges; return this; @@ -884,7 +890,8 @@ class WindowTestsBase extends SystemServiceTestsBase { aInfo.flags |= mActivityFlags; aInfo.launchMode = mLaunchMode; aInfo.resizeMode = mResizeMode; - aInfo.maxAspectRatio = mMaxAspectRatio; + aInfo.setMaxAspectRatio(mMaxAspectRatio); + aInfo.setMinAspectRatio(mMinAspectRatio); aInfo.supportsSizeChanges = mSupportsSizeChanges; aInfo.screenOrientation = mScreenOrientation; aInfo.configChanges |= mConfigChanges; diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index 72e1e33491ff..8874e0afd716 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.IBinder; -import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; @@ -204,28 +203,6 @@ public final class TranslationManagerService } } - @Override - public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) { - TranslationManagerServiceImpl service; - synchronized (mLock) { - service = getServiceForUserLocked(userId); - } - if (service != null) { - service.registerUiTranslationStateCallback(callback, Binder.getCallingUid()); - } - } - - @Override - public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) { - TranslationManagerServiceImpl service; - synchronized (mLock) { - service = getServiceForUserLocked(userId); - } - if (service != null) { - service.unregisterUiTranslationStateCallback(callback); - } - } - /** * Dump the service state into the given stream. You run "adb shell dumpsys translation". */ diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index 1ca07cb8d928..ab6ac12c90fa 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -17,24 +17,17 @@ package com.android.server.translation; import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS; -import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE; -import static android.view.translation.UiTranslationManager.EXTRA_STATE; -import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; -import android.os.Bundle; import android.os.IBinder; -import android.os.IRemoteCallback; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.service.translation.TranslationServiceInfo; import android.util.Slog; import android.view.autofill.AutofillId; -import android.view.inputmethod.InputMethodInfo; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationManager.UiTranslationState; @@ -43,7 +36,6 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.util.SyncResultReceiver; import com.android.server.LocalServices; import com.android.server.infra.AbstractPerUserSystemService; -import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens; @@ -182,50 +174,5 @@ final class TranslationManagerServiceImpl extends } catch (RemoteException e) { Slog.w(TAG, "Update UiTranslationState fail: " + e); } - invokeCallbacks(state, sourceSpec, destSpec); } - - private void invokeCallbacks( - int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) { - Bundle res = new Bundle(); - res.putInt(EXTRA_STATE, state); - // TODO(177500482): Store the locale pair so it can be sent for RESUME events. - if (sourceSpec != null) { - res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage()); - res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage()); - } - // TODO(177500482): Only support the *current* Input Method. - List<InputMethodInfo> enabledInputMethods = - LocalServices.getService(InputMethodManagerInternal.class) - .getEnabledInputMethodListAsUser(mUserId); - mCallbacks.broadcast((callback, uid) -> { - // Code here is non-optimal since it's temporary.. - boolean isIme = false; - for (InputMethodInfo inputMethod : enabledInputMethods) { - if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) { - isIme = true; - } - } - // TODO(177500482): Invoke it for the application being translated too. - if (!isIme) { - return; - } - try { - callback.sendResult(res); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); - } - }); - } - - public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) { - mCallbacks.register(callback, sourceUid); - // TODO(177500482): trigger the callback here if we're already translating the UI. - } - - public void unregisterUiTranslationStateCallback(IRemoteCallback callback) { - mCallbacks.unregister(callback); - } - - private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1a71f808daa7..45eafa45c78d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -33,6 +33,8 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; +import android.telephony.gba.TlsParams; +import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.ImsSsData; @@ -3696,6 +3698,70 @@ public class CarrierConfigManager { */ public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; + /** + * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_ME = 1; + + /** + * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_U = 2; + + /** + * Indicates that GBA_Digest should be used for GBA authentication, as defined + * in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_DIGEST = 3; + + /** + * An integer representing the GBA mode to use for requesting credentials + * via {@link TelephonyManager#bootstrapAuthenticationRequest}. + * + * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + + /** + * An integer representing the organization code to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = + "gba_ua_security_organization_int"; + + /** + * An integer representing the security protocol to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = + "gba_ua_security_protocol_int"; + + /** + * An integer representing the cipher suite to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = + "gba_ua_tls_cipher_suite_int"; /** * Configs used by ImsServiceEntitlement. @@ -4713,6 +4779,16 @@ public class CarrierConfigManager { public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY = "allowed_initial_attach_apn_types_string_array"; + /** + * Indicates whether or not the carrier will provision merged carrier Wi-Fi offload networks. + * Such networks are considered part of the core carrier network. + * + * This configuration will be use to gate whether such configurations are allowed to the carrier + * and correspondingly enable UI elements which are required for such configurations. + */ + public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = + "carrier_provisions_wifi_merged_networks_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -5255,6 +5331,13 @@ public class CarrierConfigManager { // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); + sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME); + sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT, + UaSecurityProtocolIdentifier.ORG_3GPP); + sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT, + UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT); + sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL); + sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, @@ -5276,6 +5359,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false); sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, new String[]{"ia", "default", "ims", "mms", "dun", "emergency"}); + sDefaults.putBoolean(KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL, false); } /** diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.aidl b/telephony/java/android/telephony/LinkCapacityEstimate.aidl new file mode 100644 index 000000000000..286f33fc9810 --- /dev/null +++ b/telephony/java/android/telephony/LinkCapacityEstimate.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable LinkCapacityEstimate;
\ No newline at end of file diff --git a/telephony/java/android/telephony/LinkCapacityEstimate.java b/telephony/java/android/telephony/LinkCapacityEstimate.java new file mode 100644 index 000000000000..deeb80961c3c --- /dev/null +++ b/telephony/java/android/telephony/LinkCapacityEstimate.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Link Capacity Estimate from the modem + * @hide + */ +@SystemApi +public final class LinkCapacityEstimate implements Parcelable { + /** A value indicates that the capacity estimate is not available */ + public static final int INVALID = -1; + + /** + * LCE for the primary network + */ + public static final int LCE_TYPE_PRIMARY = 0; + + /** + * LCE for the secondary network + */ + public static final int LCE_TYPE_SECONDARY = 1; + + /** + * Combined LCE for primary network and secondary network reported by the legacy modem + */ + public static final int LCE_TYPE_COMBINED = 2; + + /** @hide */ + @IntDef(prefix = { "LCE_TYPE_" }, value = { + LCE_TYPE_PRIMARY, + LCE_TYPE_SECONDARY, + LCE_TYPE_COMBINED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LceType {} + + private final @LceType int mType; + + /** Downlink capacity estimate in kbps */ + private final int mDownlinkCapacityKbps; + + /** Uplink capacity estimate in kbps */ + private final int mUplinkCapacityKbps; + + /** + * Constructor for link capacity estimate + */ + public LinkCapacityEstimate(@LceType int type, + int downlinkCapacityKbps, int uplinkCapacityKbps) { + mDownlinkCapacityKbps = downlinkCapacityKbps; + mUplinkCapacityKbps = uplinkCapacityKbps; + mType = type; + } + + /** + * @hide + */ + public LinkCapacityEstimate(Parcel in) { + mDownlinkCapacityKbps = in.readInt(); + mUplinkCapacityKbps = in.readInt(); + mType = in.readInt(); + } + + /** + * Retrieves the type of LCE + * @return The type of link capacity estimate + */ + public @LceType int getType() { + return mType; + } + + /** + * Retrieves the downlink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * @return The estimated first hop downstream (network to device) bandwidth. + */ + public int getDownlinkCapacityKbps() { + return mDownlinkCapacityKbps; + } + + /** + * Retrieves the uplink bandwidth in Kbps. + * This will be {@link #INVALID} if the network is not connected + * + * @return The estimated first hop upstream (device to network) bandwidth. + */ + public int getUplinkCapacityKbps() { + return mUplinkCapacityKbps; + } + + @Override + public String toString() { + return new StringBuilder() + .append("{mType=") + .append(mType) + .append(", mDownlinkCapacityKbps=") + .append(mDownlinkCapacityKbps) + .append(", mUplinkCapacityKbps=") + .append(mUplinkCapacityKbps) + .append("}") + .toString(); + } + + /** + * {@link Parcelable#describeContents} + */ + public int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + * @hide + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDownlinkCapacityKbps); + dest.writeInt(mUplinkCapacityKbps); + dest.writeInt(mType); + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == null || !(o instanceof LinkCapacityEstimate) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + LinkCapacityEstimate that = (LinkCapacityEstimate) o; + return mDownlinkCapacityKbps == that.mDownlinkCapacityKbps + && mUplinkCapacityKbps == that.mUplinkCapacityKbps + && mType == that.mType; + } + + @Override + public int hashCode() { + return Objects.hash(mDownlinkCapacityKbps, mUplinkCapacityKbps, mType); + } + + public static final + @android.annotation.NonNull Parcelable.Creator<LinkCapacityEstimate> CREATOR = + new Parcelable.Creator() { + public LinkCapacityEstimate createFromParcel(Parcel in) { + return new LinkCapacityEstimate(in); + } + + public LinkCapacityEstimate[] newArray(int size) { + return new LinkCapacityEstimate[size]; + } + }; +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3084b144c9b9..faa5deed0f1c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9980,7 +9980,8 @@ public class TelephonyManager { } /** - * Sets the roaming mode for CDMA phone to the given mode {@code mode}. + * Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method does nothing. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -10003,6 +10004,7 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(@CdmaRoamingMode int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) return; try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -10083,7 +10085,8 @@ public class TelephonyManager { } /** - * Sets the subscription mode for CDMA phone to the given mode {@code mode}. + * Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not + * CDMA capable, this method does nothing. * * @param mode CDMA subscription mode. * @throws SecurityException if the caller does not have the permission. @@ -10102,6 +10105,7 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(@CdmaSubscription int mode) { + if (getPhoneType() != PHONE_TYPE_CDMA) return; try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -14503,34 +14507,6 @@ public class TelephonyManager { } /** - * Get carrier bandwidth. In case of Dual connected network this will report - * bandwidth per primary and secondary network. It is possible that - * some modems may not fill secondary carrier bandwidth. - * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. - * @throws IllegalStateException if the Telephony process is not currently available. - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @NonNull - public CarrierBandwidth getCarrierBandwidth() { - try { - ITelephony service = getITelephony(); - if (service != null) { - return service.getCarrierBandwidth(getSubId()); - } else { - throw new IllegalStateException("telephony service is null."); - } - } catch (RemoteException ex) { - Log.e(TAG, "getCarrierBandwidth RemoteException", ex); - ex.rethrowFromSystemServer(); - } - - //Should not reach. Adding return statement to make compiler happy - return null; - } - - /** * Called when userActivity is signalled in the power manager. * This should only be called from system Uid. * @hide diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 40b86966d0e8..b30dd2697645 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -31,7 +31,6 @@ import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CallForwardingInfo; -import android.telephony.CarrierBandwidth; import android.telephony.CarrierRestrictionRules; import android.telephony.CellIdentity; import android.telephony.CellInfo; @@ -2238,12 +2237,6 @@ interface ITelephony { boolean isNrDualConnectivityEnabled(int subId); /** - * Get carrier bandwidth per primary and secondary carrier - * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. - */ - CarrierBandwidth getCarrierBandwidth(int subId); - - /** * Checks whether the device supports the given capability on the radio interface. * * @param capability the name of the capability diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 5760211d9a27..b7ece8f4c4c9 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -312,14 +312,14 @@ public class DnsManagerTest { @Test public void testOverrideDefaultMode() throws Exception { // Hard-coded default is opportunistic mode. - final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgAuto = DnsManager.getPrivateDnsConfig(mCtx); assertTrue(cfgAuto.useTls); assertEquals("", cfgAuto.hostname); assertEquals(new InetAddress[0], cfgAuto.ips); // Pretend a gservices push sets the default to "off". Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "off"); - final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgOff = DnsManager.getPrivateDnsConfig(mCtx); assertFalse(cfgOff.useTls); assertEquals("", cfgOff.hostname); assertEquals(new InetAddress[0], cfgOff.ips); @@ -328,7 +328,7 @@ public class DnsManagerTest { Settings.Global.putString( mContentResolver, PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com"); - final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mContentResolver); + final PrivateDnsConfig cfgStrict = DnsManager.getPrivateDnsConfig(mCtx); assertTrue(cfgStrict.useTls); assertEquals("strictmode.com", cfgStrict.hostname); assertEquals(new InetAddress[0], cfgStrict.ips); |