diff options
145 files changed, 3189 insertions, 1283 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index b3c33b6615f4..144536ec2644 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1229,7 +1229,11 @@ public class JobInfo implements Parcelable { * </ul> * Note that the system may choose to delay jobs with large network * usage estimates when the device has a poor network connection, in - * order to save battery. + * order to save battery and possible network costs. + * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt + * to run large jobs when the device is charging and on an unmetered network, even if the + * network is slow. This gives large jobs an opportunity to make forward progress, even if + * they risk timing out. * <p> * The values provided here only reflect the traffic that will be * performed by the base job; if you're using {@link JobWorkItem} then diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 60f5769a46f7..cef065ddac9e 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -547,7 +547,7 @@ public class DeviceIdleController extends SystemService private int[] mPowerSaveWhitelistUserAppIdArray = new int[0]; /** - * List of end times for UIDs that are temporarily marked as being allowed to access + * List of end times for app-IDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 5365218203d9..1169391d2cd2 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -121,7 +121,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; -import com.android.internal.os.BinderDeathDispatcher; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LocalLog; @@ -184,8 +183,7 @@ public class AlarmManagerService extends SystemService { static final boolean DEBUG_BG_LIMIT = localLOGV || false; static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; - // TODO(b/178484639) : Turn off once alarms and reminders work is complete. - static final boolean RECORD_DEVICE_IDLE_ALARMS = true; + static final boolean RECORD_DEVICE_IDLE_ALARMS = false; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; static final int TICK_HISTORY_DEPTH = 10; @@ -206,8 +204,6 @@ public class AlarmManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher = - new BinderDeathDispatcher<>(); final LocalLog mLog = new LocalLog(TAG); AppOpsManager mAppOps; @@ -1837,8 +1833,9 @@ public class AlarmManagerService extends SystemService { } if (directReceiver != null) { - if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient) - <= 0) { + try { + directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0); + } catch (RemoteException e) { Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag); return; } @@ -2851,12 +2848,6 @@ public class AlarmManagerService extends SystemService { pw.println(); } - pw.println("Listener death dispatcher state:"); - pw.increaseIndent(); - sListenerDeathDispatcher.dump(pw); - pw.println(); - pw.decreaseIndent(); - if (mLog.dump(pw, "Recent problems:")) { pw.println(); } @@ -3448,6 +3439,9 @@ public class AlarmManagerService extends SystemService { for (final Alarm removed : removedAlarms) { decrementAlarmCount(removed.uid, 1); + if (removed.listener != null) { + removed.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0); + } if (!RemovedAlarm.isLoggable(reason)) { continue; } @@ -4701,6 +4695,8 @@ public class AlarmManagerService extends SystemService { // Direct listener callback alarm mListenerCount++; + alarm.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0); + if (RECORD_ALARMS_IN_HISTORY) { if (alarm.listener == mTimeTickTrigger) { mTickHistory[mNextTickHistory++] = nowELAPSED; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 7b947fd3ab43..e8065aa535f3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -25,12 +25,18 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -101,6 +107,8 @@ public final class ConnectivityController extends RestrictingController implemen private final ConnectivityManager mConnManager; private final NetworkPolicyManagerInternal mNetPolicyManagerInternal; + private final ChargingTracker mChargingTracker; + /** List of tracked jobs keyed by source UID. */ @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>(); @@ -229,6 +237,9 @@ public final class ConnectivityController extends RestrictingController implemen // network changes against the active network for each UID with jobs. final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mConnManager.registerNetworkCallback(request, mNetworkCallback); + + mChargingTracker = new ChargingTracker(); + mChargingTracker.startTracking(); } @GuardedBy("mLock") @@ -538,6 +549,14 @@ public final class ConnectivityController extends RestrictingController implemen */ private boolean isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { + if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED) + && mChargingTracker.isCharging()) { + // We're charging and on an unmetered network. We don't have to be as conservative about + // making sure the job will run within its max execution time. Let's just hope the app + // supports interruptible work. + return false; + } + // Use the maximum possible time since it gives us an upper bound, even though the job // could end up stopping earlier. final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus); @@ -922,7 +941,7 @@ public final class ConnectivityController extends RestrictingController implemen * {@link Network}, or {@code null} to update all tracked jobs. */ @GuardedBy("mLock") - private void updateTrackedJobsLocked(int filterUid, Network filterNetwork) { + private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) { boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { @@ -937,7 +956,8 @@ public final class ConnectivityController extends RestrictingController implemen } @GuardedBy("mLock") - private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) { + private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, + @Nullable Network filterNetwork) { if (jobs == null || jobs.size() == 0) { return false; } @@ -993,6 +1013,51 @@ public final class ConnectivityController extends RestrictingController implemen } } + private final class ChargingTracker extends BroadcastReceiver { + /** + * Track whether we're "charging", where charging means that we're ready to commit to + * doing work. + */ + private boolean mCharging; + + ChargingTracker() {} + + public void startTracking() { + IntentFilter filter = new IntentFilter(); + filter.addAction(BatteryManager.ACTION_CHARGING); + filter.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(this, filter); + + // Initialise tracker state. + final BatteryManagerInternal batteryManagerInternal = + LocalServices.getService(BatteryManagerInternal.class); + mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); + } + + public boolean isCharging() { + return mCharging; + } + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + final String action = intent.getAction(); + if (BatteryManager.ACTION_CHARGING.equals(action)) { + if (mCharging) { + return; + } + mCharging = true; + } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { + if (!mCharging) { + return; + } + mCharging = false; + } + updateTrackedJobsLocked(-1, null); + } + } + } + private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onAvailable(Network network) { diff --git a/core/api/current.txt b/core/api/current.txt index 002b3334d731..4c61afea3b59 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -31964,7 +31964,6 @@ package android.os { method public long getUserCreationTime(android.os.UserHandle); method public android.os.UserHandle getUserForSerialNumber(long); method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName(); - method public int getUserPrivacySensitivity(); method public java.util.List<android.os.UserHandle> getUserProfiles(); method public android.os.Bundle getUserRestrictions(); method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); @@ -32045,8 +32044,6 @@ package android.os { field public static final String DISALLOW_USER_SWITCH = "no_user_switch"; field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps"; field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; - field public static final int PRIVACY_SENSITIVITY_DEFAULT = 0; // 0x0 - field public static final int PRIVACY_SENSITIVITY_LOCATION = 1; // 0x1 field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1 field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1 field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f23966e8d164..7072bbeade29 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4930,10 +4930,6 @@ package android.location { field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; } - public final class LocationDeviceConfig { - field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; - } - public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); @@ -4946,7 +4942,6 @@ package android.location { method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle); method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String); - method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); @@ -12843,7 +12838,6 @@ package android.telephony.ims { method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>); method public void onDestroyed(int); method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState); - method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); } public final class FeatureTagState implements android.os.Parcelable { @@ -13664,67 +13658,6 @@ package android.telephony.ims { method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); } - @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { - method @Deprecated public boolean containsKey(@NonNull String); - method @Deprecated @NonNull public android.os.PersistableBundle copyBundle(); - method @Deprecated public int describeContents(); - method @Deprecated public boolean getBoolean(@NonNull String, boolean); - method @Deprecated public int getInt(@NonNull String, int); - method @Deprecated @Nullable public String getString(@NonNull String); - method @Deprecated public long getVersion(); - method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; - field @Deprecated public static final String IPTYPE_IPV4 = "IPV4"; - field @Deprecated public static final String IPTYPE_IPV6 = "IPV6"; - field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; - field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP"; - field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP"; - } - - @Deprecated public static final class SipDelegateImsConfiguration.Builder { - ctor @Deprecated public SipDelegateImsConfiguration.Builder(int); - ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); - } - public class SipDelegateManager { method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); @@ -13892,11 +13825,10 @@ package android.telephony.ims.stub { } public interface DelegateConnectionStateCallback { - method public default void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration); + method public void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration); method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection); method public void onDestroyed(int); method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>); - method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); } public class ImsCallSessionImplBase implements java.lang.AutoCloseable { diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index 7cf007640b1a..9a8a49397322 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -135,6 +135,7 @@ package android.location { public class LocationManager { method @Deprecated public boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @Deprecated public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackage(String); @@ -231,6 +232,83 @@ package android.telephony.data { } +package android.telephony.ims { + + public interface DelegateStateCallback { + method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + + @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { + method public boolean containsKey(@NonNull String); + method @NonNull public android.os.PersistableBundle copyBundle(); + method public int describeContents(); + method public boolean getBoolean(@NonNull String, boolean); + method public int getInt(@NonNull String, int); + method @Nullable public String getString(@NonNull String); + method public long getVersion(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field public static final String IPTYPE_IPV4 = "IPV4"; + field public static final String IPTYPE_IPV6 = "IPV6"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; + field public static final String SIP_TRANSPORT_TCP = "TCP"; + field public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + public static final class SipDelegateImsConfiguration.Builder { + ctor public SipDelegateImsConfiguration.Builder(int); + ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + } + +} + +package android.telephony.ims.stub { + + public interface DelegateConnectionStateCallback { + method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + +} + package android.view.translation { public final class UiTranslationManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 8499b3739528..2f795f0e103f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1360,10 +1360,6 @@ package android.location { method public void setType(int); } - public final class LocationDeviceConfig { - field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; - } - public class LocationManager { method @NonNull public String[] getBackgroundThrottlingWhitelist(); method @NonNull public android.os.PackageTagsList getIgnoreSettingsAllowlist(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8d39d8aa1261..7149096ee806 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -237,7 +237,6 @@ import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** @@ -338,6 +337,11 @@ public final class ActivityThread extends ClientTransactionHandler */ @UnsupportedAppUsage final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); + /** + * Maps from activity token to local record of the activities that are preparing to be launched. + */ + final Map<IBinder, ActivityClientRecord> mLaunchingActivities = + Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>()); /** The activities to be truly destroyed (not include relaunch). */ final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed = Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>()); @@ -347,7 +351,6 @@ public final class ActivityThread extends ClientTransactionHandler // Number of activities that are currently visible on-screen. @UnsupportedAppUsage int mNumVisibleActivities = 0; - private final AtomicInteger mNumLaunchingActivities = new AtomicInteger(); @GuardedBy("mAppThread") private int mLastProcessState = PROCESS_STATE_UNKNOWN; @GuardedBy("mAppThread") @@ -3256,6 +3259,21 @@ public final class ActivityThread extends ClientTransactionHandler } @Override + public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) { + mLaunchingActivities.put(token, activity); + } + + @Override + public ActivityClientRecord getLaunchingActivity(IBinder token) { + return mLaunchingActivities.get(token); + } + + @Override + public void removeLaunchingActivity(IBinder token) { + mLaunchingActivities.remove(token); + } + + @Override public ActivityClientRecord getActivityClient(IBinder token) { return mActivities.get(token); } @@ -3299,7 +3317,7 @@ public final class ActivityThread extends ClientTransactionHandler // Defer the top state for VM to avoid aggressive JIT compilation affecting activity // launch time. if (processState == ActivityManager.PROCESS_STATE_TOP - && mNumLaunchingActivities.get() > 0) { + && !mLaunchingActivities.isEmpty()) { mPendingProcessState = processState; mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT); } else { @@ -3315,7 +3333,7 @@ public final class ActivityThread extends ClientTransactionHandler // Handle the pending configuration if the process state is changed from cached to // non-cached. Except the case where there is a launching activity because the // LaunchActivityItem will handle it. - if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) { + if (wasCached && !isCachedProcessState() && mLaunchingActivities.isEmpty()) { final Configuration pendingConfig = mConfigurationController.getPendingConfiguration(false /* clearPending */); if (pendingConfig == null) { @@ -3353,11 +3371,6 @@ public final class ActivityThread extends ClientTransactionHandler } } - @Override - public void countLaunchingActivities(int num) { - mNumLaunchingActivities.getAndAdd(num); - } - @UnsupportedAppUsage public final void sendActivityResult( IBinder token, String id, int requestCode, diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index c752f34ab0bb..115101c0bff6 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -82,9 +82,6 @@ public abstract class ClientTransactionHandler { /** Set current process state. */ public abstract void updateProcessState(int processState, boolean fromIpc); - /** Count how many activities are launching. */ - public abstract void countLaunchingActivities(int num); - // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions // and deliver callbacks. @@ -193,6 +190,26 @@ public abstract class ClientTransactionHandler { FixedRotationAdjustments fixedRotationAdjustments); /** + * Add {@link ActivityClientRecord} that is preparing to be launched. + * @param token Activity token. + * @param activity An initialized instance of {@link ActivityClientRecord} to use during launch. + */ + public abstract void addLaunchingActivity(IBinder token, ActivityClientRecord activity); + + /** + * Get {@link ActivityClientRecord} that is preparing to be launched. + * @param token Activity token. + * @return An initialized instance of {@link ActivityClientRecord} to use during launch. + */ + public abstract ActivityClientRecord getLaunchingActivity(IBinder token); + + /** + * Remove {@link ActivityClientRecord} from the launching activity list. + * @param token Activity token. + */ + public abstract void removeLaunchingActivity(IBinder token); + + /** * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the * provided token. */ diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 47ababf71e50..432d99d80b89 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3314,6 +3314,19 @@ public class Notification implements Parcelable } /** + * Sets the token used for background operations for the pending intents associated with this + * notification. + * + * This token is automatically set during deserialization for you, you usually won't need to + * call this unless you want to change the existing token, if any. + * + * @hide + */ + public void setAllowlistToken(@Nullable IBinder token) { + mAllowlistToken = token; + } + + /** * @hide */ public static void addFieldsFromContext(Context context, Notification notification) { diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index d5585db08994..032b57e65458 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -40,7 +40,8 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { @Override public void preExecute(android.app.ClientTransactionHandler client, IBinder token) { - final ActivityClientRecord r = getActivityClientRecord(client, token); + final ActivityClientRecord r = getActivityClientRecord(client, token, + true /* includeLaunching */); // Notify the client of an upcoming change in the token configuration. This ensures that // batches of config change items only process the newest configuration. client.updatePendingActivityConfiguration(r, mConfiguration); diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java index f7d7e9d20ab9..a539812fa8c6 100644 --- a/core/java/android/app/servertransaction/ActivityTransactionItem.java +++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java @@ -55,15 +55,40 @@ public abstract class ActivityTransactionItem extends ClientTransactionItem { @NonNull ActivityClientRecord getActivityClientRecord( @NonNull ClientTransactionHandler client, IBinder token) { - final ActivityClientRecord r = client.getActivityClient(token); + return getActivityClientRecord(client, token, false /* includeLaunching */); + } + + /** + * Get the {@link ActivityClientRecord} instance that corresponds to the provided token. + * @param client Target client handler. + * @param token Target activity token. + * @param includeLaunching Indicate to also find the {@link ActivityClientRecord} in launching + * activity list. It should be noted that there is no activity in + * {@link ActivityClientRecord} from the launching activity list. + * @return The {@link ActivityClientRecord} instance that corresponds to the provided token. + */ + @NonNull ActivityClientRecord getActivityClientRecord( + @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) { + ActivityClientRecord r = client.getActivityClient(token); + if (r != null) { + if (client.getActivity(token) == null) { + throw new IllegalArgumentException("Activity must not be null to execute " + + "transaction item"); + } + return r; + } + // The activity may not be launched yet. Fallback to check launching activity. + if (includeLaunching) { + r = client.getLaunchingActivity(token); + } if (r == null) { throw new IllegalArgumentException("Activity client record must not be null to execute " + "transaction item"); } - if (client.getActivity(token) == null) { - throw new IllegalArgumentException("Activity must not be null to execute " - + "transaction item"); - } + + // We don't need to check the activity of launching activity client records because they + // have not been launched yet. + return r; } } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index e281a0268184..34e4fcdb9140 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -82,7 +82,12 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void preExecute(ClientTransactionHandler client, IBinder token) { - client.countLaunchingActivities(1); + ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, + mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, + mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, + client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken, + mLaunchedFromBubble); + client.addLaunchingActivity(token, r); client.updateProcessState(mProcState, false); client.updatePendingConfiguration(mCurConfig); if (mActivityClientController != null) { @@ -94,11 +99,7 @@ public class LaunchActivityItem extends ClientTransactionItem { public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); - ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo, - mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, - mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, - client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken, - mLaunchedFromBubble); + ActivityClientRecord r = client.getLaunchingActivity(token); client.handleLaunchActivity(r, pendingActions, null /* customIntent */); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @@ -106,7 +107,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { - client.countLaunchingActivities(-1); + client.removeLaunchingActivity(token); } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index 944367e304e6..4b8a3476262e 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -40,7 +40,8 @@ public class MoveToDisplayItem extends ActivityTransactionItem { @Override public void preExecute(ClientTransactionHandler client, IBinder token) { - final ActivityClientRecord r = getActivityClientRecord(client, token); + final ActivityClientRecord r = getActivityClientRecord(client, token, + true /* includeLaunching */); // Notify the client of an upcoming change in the token configuration. This ensures that // batches of config change items only process the newest configuration. client.updatePendingActivityConfiguration(r, mConfiguration); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fc676cf5b9c2..886cd7f48d35 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -400,10 +400,7 @@ public abstract class Context { public static final int BIND_BYPASS_POWER_NETWORK_RESTRICTIONS = 0x00020000; /** - * Flag for {@link #bindService}: allow background foreground service starts from the bound - * service's process. - * This flag is only respected if the caller is holding - * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}. + * Do not use. This flag is no longer needed nor used. * @hide */ @SystemApi diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 33606e855220..33a34be1b968 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4672,27 +4672,27 @@ public abstract class PackageManager { @PermissionGroupInfoFlags int flags); /** - * Get the platform permissions which belong to a particular permission group. + * Get the platform-defined permissions which belong to a particular permission group. * - * @param permissionGroupName The permission group whose permissions are desired - * @param executor Executor on which to invoke the callback - * @param callback A callback which will receive a list of the platform permissions in the - * group, or empty if the group is not a valid platform group, or there - * was an exception. + * @param permissionGroupName the permission group whose permissions are desired + * @param executor the {@link Executor} on which to invoke the callback + * @param callback the callback which will receive a list of the platform-defined permissions in + * the group, or empty if the group is not a valid platform-defined permission + * group, or there was an exception */ public void getPlatformPermissionsForGroup(@NonNull String permissionGroupName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<List<String>> callback) {} /** - * Get the platform group of a particular permission, if the permission is a platform - * permission. + * Get the platform-defined permission group of a particular permission, if the permission is a + * platform-defined permission. * - * @param permissionName The permission name whose group is desired - * @param executor Executor on which to invoke the callback - * @param callback A callback which will receive the name of the permission group this - * permission belongs to, or null if it has no group, is not a platform - * permission, or there was an exception. + * @param permissionName the permission whose group is desired + * @param executor the {@link Executor} on which to invoke the callback + * @param callback the callback which will receive the name of the permission group this + * permission belongs to, or {@code null} if it has no group, is not a + * platform-defined permission, or there was an exception */ public void getGroupOfPlatformPermission(@NonNull String permissionName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<String> callback) {} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f0d410f46f81..881e0cfb58d7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -481,6 +481,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean mFullscreenApplied; boolean mIsFullscreen; + private boolean mLastWasInFullscreenMode; @UnsupportedAppUsage View mExtractView; boolean mExtractViewHidden; @@ -920,8 +921,17 @@ public class InputMethodService extends AbstractInputMethodService { if (mHandler == null) { mHandler = new Handler(getMainLooper()); } - mImeSurfaceScheduledForRemoval = true; - mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + + if (mLastWasInFullscreenMode) { + // Caching surface / delaying surface removal can cause mServedView to detach in certain + // cases in RecyclerView (b/187772544). + // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching + // view issues is resolved in RecyclerView. + removeImeSurface(); + } else { + mImeSurfaceScheduledForRemoval = true; + mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + } } private void removeImeSurface() { @@ -2350,6 +2360,7 @@ public class InputMethodService extends AbstractInputMethodService { onWindowHidden(); mDecorViewWasVisible = false; } + mLastWasInFullscreenMode = mIsFullscreen; updateFullscreenMode(); } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 370052d47d16..f2857ceb59d7 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -368,27 +368,19 @@ public final class BatteryUsageStats implements Parcelable { }; /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */ - public byte[] getStatsProto(long sessionEndTimestampMs) { - - final long sessionStartMillis = getStatsStartTimestamp(); - // TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added. - final long sessionEndMillis = sessionEndTimestampMs; - final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp(); - + public byte[] getStatsProto() { final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer( AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - final int sessionDischargePercentage = getDischargePercentage(); - final ProtoOutputStream proto = new ProtoOutputStream(); - proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis); - proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis); - proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis); + proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp()); + proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp()); + proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration()); deviceBatteryConsumer.writeStatsProto(proto, BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); writeUidBatteryConsumersProto(proto); proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE, - sessionDischargePercentage); + getDischargePercentage()); return proto.getBytes(); } @@ -399,8 +391,8 @@ public final class BatteryUsageStats implements Parcelable { private void writeUidBatteryConsumersProto(ProtoOutputStream proto) { final List<UidBatteryConsumer> consumers = getUidBatteryConsumers(); - // TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb, - // truncate the remainder of the list. + // TODO(b/189225426): Sort the list by power consumption. If during the for, + // proto.getRawSize() > 45kb, truncate the remainder of the list. final int size = consumers.size(); for (int i = 0; i < size; i++) { final UidBatteryConsumer consumer = consumers.get(i); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 5848d2f1af2c..224cd84bc777 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -189,28 +189,6 @@ public class UserManager { public @interface QuietModeFlag {} /** - * Flag returned by {@link #getUserPrivacySensitivity} to indicate that the user isn't - * particularly sensitive about a certain aspect of privacy. - */ - public static final int PRIVACY_SENSITIVITY_DEFAULT = 0x0; - - /** - * Flag returned by {@link #getUserPrivacySensitivity} to indicate that the user is sensitive - * about location privacy. - */ - public static final int PRIVACY_SENSITIVITY_LOCATION = 0x1; - - /** - * List of flags available for the {@link #getUserPrivacySensitivity} method. - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "PRIVACY_SENSITIVITY_" }, value = { - PRIVACY_SENSITIVITY_DEFAULT, - PRIVACY_SENSITIVITY_LOCATION}) - public @interface PrivacySensitivityFlag {} - - /** * @hide * No user restriction. */ @@ -3960,17 +3938,6 @@ public class UserManager { } /** - * Get the privacy sensitivity of the user. - * - * @return the privacy sensitivity of the user - */ - @PrivacySensitivityFlag - public int getUserPrivacySensitivity() { - // TODO: Add actual implementation. - return PRIVACY_SENSITIVITY_DEFAULT; - } - - /** * Returns whether the given user has a badge (generally to put on profiles' icons). * * @param userId userId of the user in question diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java index 01cece39b922..c82a51669b86 100644 --- a/core/java/android/os/VibratorManager.java +++ b/core/java/android/os/VibratorManager.java @@ -94,6 +94,8 @@ public abstract class VibratorManager { * VibrationEffect VibrationEffects} to be played on one or more vibrators. * </p> * + * <p>The app should be in foreground for the vibration to happen.</p> + * * @param effect a combination of effects to be performed by one or more vibrators. */ @RequiresPermission(android.Manifest.permission.VIBRATE) @@ -109,6 +111,9 @@ public abstract class VibratorManager { * VibrationEffect} to be played on one or more vibrators. * </p> * + * <p>The app should be in foreground for the vibration to happen. Background apps should + * specify a ringtone, notification or alarm usage in order to vibrate.</p> + * * @param effect a combination of effects to be performed by one or more vibrators. * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example, * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5cfb665745a8..e3302d1ca9a0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9850,12 +9850,10 @@ public final class Settings { public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption"; /** - * Controls which packages are blocked from persisting in media controls when resumption is - * enabled. The list of packages is set by the user in the Settings app. - * @see Settings.Secure#MEDIA_CONTROLS_RESUME + * Controls whether contextual suggestions can be shown in the media controls. * @hide */ - public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked"; + public static final String MEDIA_CONTROLS_RECOMMENDATION = "qs_media_recommend"; /** * Controls magnification mode when magnification is enabled via a system-wide triple tap diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0acacb60da3f..908d236c6c02 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9828,30 +9828,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mContext.getContentCaptureOptions() == null) return; if (appeared) { - if (!isLaidOut() || getVisibility() != VISIBLE - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { + // The appeared event stops sending to AiAi. + // 1. The view is hidden. + // 2. The same event was sent. + // 3. The view is not laid out, and it will be laid out in the future. + // Some recycled views cached its layout and a relayout is unnecessary. In this case, + // system still needs to notify content capture the view appeared. When a view is + // recycled, it will set the flag PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED. + final boolean isRecycledWithoutRelayout = getNotifiedContentCaptureDisappeared() + && getVisibility() == VISIBLE + && !isLayoutRequested(); + if (getVisibility() != VISIBLE || getNotifiedContentCaptureAppeared() + || !(isLaidOut() || isRecycledWithoutRelayout)) { if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared() + + ", alreadyNotifiedDisappeared=" + + getNotifiedContentCaptureDisappeared()); } return; } } else { - if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { + if (!getNotifiedContentCaptureAppeared() || getNotifiedContentCaptureDisappeared()) { if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared() + + ", alreadyNotifiedDisappeared=" + + getNotifiedContentCaptureDisappeared()); } return; } @@ -9899,6 +9906,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } + private boolean getNotifiedContentCaptureDisappeared() { + return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0; + } + /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 0d041c4440d2..e693d9dd9a7d 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -201,7 +201,10 @@ public class CpuPowerCalculator extends PowerCalculator { result.packageWithHighestDrain = packageWithHighestDrain; } - private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { + /** + * Calculates CPU power consumed by the specified app, using the PowerProfile model. + */ + public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { // Constant battery drain when CPU is active double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index a26abc2ffab0..b4a2b637cae8 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -40,8 +40,10 @@ public class SystemServicePowerCalculator extends PowerCalculator { // to this layout: // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} private final UsageBasedPowerEstimator[] mPowerEstimators; + private final CpuPowerCalculator mCpuPowerCalculator; public SystemServicePowerCalculator(PowerProfile powerProfile) { + mCpuPowerCalculator = new CpuPowerCalculator(powerProfile); int numFreqs = 0; final int numCpuClusters = powerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { @@ -62,7 +64,22 @@ public class SystemServicePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - double systemServicePowerMah = calculateSystemServicePower(batteryStats); + final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); + if (systemUid == null) { + return; + } + + final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + + double systemServicePowerMah; + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, + systemUid, consumptionUC); + } else { + systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); + } + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = builder.getUidBatteryConsumerBuilders(); final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get( @@ -76,7 +93,7 @@ public class SystemServicePowerCalculator extends PowerCalculator { // distributed to applications systemServerConsumer.setConsumedPower( BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, - -systemServicePowerMah); + -systemServicePowerMah, powerModel); } for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { @@ -84,7 +101,8 @@ public class SystemServicePowerCalculator extends PowerCalculator { if (app != systemServerConsumer) { final BatteryStats.Uid uid = app.getBatteryStatsUid(); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, - systemServicePowerMah * uid.getProportionalSystemServiceUsage()); + systemServicePowerMah * uid.getProportionalSystemServiceUsage(), + powerModel); } } @@ -102,7 +120,20 @@ public class SystemServicePowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - double systemServicePowerMah = calculateSystemServicePower(batteryStats); + final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); + if (systemUid == null) { + return; + } + + final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); + double systemServicePowerMah; + if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, + systemUid, consumptionUC); + } else { + systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); + } + BatterySipper systemServerSipper = null; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper app = sippers.get(i); @@ -134,7 +165,21 @@ public class SystemServicePowerCalculator extends PowerCalculator { } } - private double calculateSystemServicePower(BatteryStats batteryStats) { + private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats, + BatteryStats.Uid systemUid, long consumptionUC) { + // Use the PowerProfile based model to estimate the ratio between the power consumed + // while handling incoming binder calls and the entire System UID power consumption. + // Apply that ratio to the _measured_ system UID power consumption to get a more + // accurate estimate of the power consumed by incoming binder calls. + final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats); + final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah( + systemUid, BatteryStats.STATS_SINCE_CHARGED); + + return uCtoMah(consumptionUC) * systemServiceModeledPowerMah + / systemUidModeledPowerMah; + } + + private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) { final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); if (systemServiceTimeAtCpuSpeeds == null) { return 0; @@ -145,13 +190,12 @@ public class SystemServicePowerCalculator extends PowerCalculator { double powerMah = 0; final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length); for (int i = 0; i < size; i++) { - powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]); + powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000); } if (DEBUG) { Log.d(TAG, "System service power:" + powerMah); } - return powerMah; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 084d4d1c0c42..f457c5626fba 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2890,7 +2890,13 @@ <permission android:name="android.permission.SET_DISPLAY_OFFSET" android:protectionLevel="signature|privileged" /> - <!-- Allows a companion app to run in the background. + <!-- Allows a companion app to run in the background. This permission implies + {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND}, + and allows to start a foreground service from the background. + If an app does not have to run in the background, but only needs to start a foreground + service from the background, consider using + {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND}, + which is less powerful. <p>Protection level: normal --> <permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" @@ -2900,6 +2906,7 @@ <!-- Allows a companion app to start a foreground service from the background. {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} + <p>Protection level: normal --> <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND" android:protectionLevel="normal"/> diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java index bee0a0bf1fd6..333eebb86d4c 100644 --- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java +++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java @@ -48,9 +48,8 @@ public class BatteryUsageStatsPulledTest { @Test public void testGetStatsProto() { - final long sessionEndTimestampMs = 1050; final BatteryUsageStats bus = buildBatteryUsageStats(); - final byte[] bytes = bus.getStatsProto(sessionEndTimestampMs); + final byte[] bytes = bus.getStatsProto(); BatteryUsageStatsAtomsProto proto; try { proto = BatteryUsageStatsAtomsProto.parseFrom(bytes); @@ -60,9 +59,9 @@ public class BatteryUsageStatsPulledTest { } assertEquals(bus.getStatsStartTimestamp(), proto.sessionStartMillis); - assertEquals(sessionEndTimestampMs, proto.sessionEndMillis); + assertEquals(bus.getStatsEndTimestamp(), proto.sessionEndMillis); assertEquals( - sessionEndTimestampMs - bus.getStatsStartTimestamp(), + bus.getStatsEndTimestamp() - bus.getStatsStartTimestamp(), proto.sessionDurationMillis); assertEquals(bus.getDischargePercentage(), proto.sessionDischargePercentage); diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index cd45060b7cc4..a36d9fe94b46 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -18,25 +18,28 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.os.BatteryConsumer; -import android.os.BatteryStats; -import android.os.BatteryUsageStatsQuery; import android.os.Binder; import android.os.Process; -import android.os.UidBatteryConsumer; -import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.power.MeasuredEnergyStats; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +50,8 @@ import java.util.Collection; public class SystemServicePowerCalculatorTest { private static final double PRECISION = 0.000001; + private static final int APP_UID1 = 100; + private static final int APP_UID2 = 200; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() @@ -61,73 +66,52 @@ public class SystemServicePowerCalculatorTest { .setAveragePowerForCpuCore(1, 0, 500) .setAveragePowerForCpuCore(1, 1, 600); + @Mock private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader; + @Mock + private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader; + + private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{ + mock(KernelCpuSpeedReader.class), + mock(KernelCpuSpeedReader.class), + }; + private MockBatteryStatsImpl mMockBatteryStats; - private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; - private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader; @Before public void setUp() throws IOException { - mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class); - mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader(); - mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); + MockitoAnnotations.initMocks(this); mMockBatteryStats = mStatsRule.getBatteryStats() - .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) + .setUserInfoProvider(mMockUserInfoProvider) + .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders) .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) - .setUserInfoProvider(mMockUserInfoProvider); + .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader) + .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader) + .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader) + .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader); } @Test public void testPowerProfileBasedModel() { - when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); - - // Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total - mMockSystemServerCpuThreadReader.setCpuTimes( - new long[] {30000, 40000, 50000, 60000}, - new long[] {20000, 30000, 40000, 50000}); - - mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( - new long[] {10000, 20000, 30000, 40000} - ); - - mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false, null); - - int workSourceUid1 = 100; - int workSourceUid2 = 200; - int transactionCode = 42; - - Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); - BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1, - Binder.class, transactionCode, true /*screenInteractive */); - stat1.incrementalCallCount = 100; - stat1.recordedCallCount = 100; - stat1.cpuTimeMicros = 1000000; - callStats.add(stat1); - - mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats); - - callStats.clear(); - BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, - Binder.class, transactionCode, true /*screenInteractive */); - stat2.incrementalCallCount = 100; - stat2.recordedCallCount = 100; - stat2.cpuTimeMicros = 9000000; - callStats.add(stat2); - - mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats); - - mMockBatteryStats.updateSystemServiceCallStats(); - mMockBatteryStats.updateSystemServerThreadStats(); + prepareBatteryStats(null); SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( mStatsRule.getPowerProfile()); - mStatsRule.apply(new FakeCpuPowerCalculator(), calculator); + mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator); - assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1) + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .isWithin(PRECISION).of(1.888888); - assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2) + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .isWithin(PRECISION).of(17.0); assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) @@ -141,58 +125,105 @@ public class SystemServicePowerCalculatorTest { .isWithin(PRECISION).of(18.888888); } - private static class MockKernelCpuUidFreqTimeReader extends - KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { - private long[] mSystemServerCpuTimes; - - MockKernelCpuUidFreqTimeReader() { - super(/*throttle */false); - } - - void setSystemServerCpuTimes(long[] systemServerCpuTimes) { - mSystemServerCpuTimes = systemServerCpuTimes; - } - - @Override - public boolean perClusterTimesAvailable() { - return true; - } - - @Override - public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) { - if (cb != null) { - cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); - } - } - } + @Test + public void testMeasuredEnergyBasedModel() { + final boolean[] supportedPowerBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + mStatsRule.getBatteryStats() + .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]); - private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader { - private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + prepareBatteryStats(new long[]{50000000, 100000000}); - MockSystemServerCpuThreadReader() { - super(null); - } + SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( + mStatsRule.getPowerProfile()); - public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { - mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; - mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; - } + mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator); - @Override - public SystemServiceCpuThreadTimes readDelta() { - return mThreadTimes; - } + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(1.979351); + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(17.814165); + assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS)) + .isWithin(PRECISION).of(-19.793517); + assertThat(mStatsRule.getDeviceBatteryConsumer() + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(19.793517); + assertThat(mStatsRule.getAppsBatteryConsumer() + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(19.793517); } - private static class FakeCpuPowerCalculator extends PowerCalculator { - @Override - protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - if (u.getUid() == Process.SYSTEM_UID) { - // SystemServer must be attributed at least as much power as the total - // of all system services requested by apps. - app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000); - } - } + private void prepareBatteryStats(long[] clusterChargesUc) { + when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); + + when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000}); + when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000}); + + when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false); + + // User/System CPU time in microseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000}); + callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000}); + callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000}); + return null; + }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any()); + + // Active CPU time in milliseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, 1111L); + callback.onUidCpuTime(APP_UID2, 3333L); + callback.onUidCpuTime(Process.SYSTEM_UID, 10000L); + return null; + }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any()); + + // Per-cluster CPU time in milliseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222}); + callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444}); + callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000}); + return null; + }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any()); + + // System service CPU time + final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes = + new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes(); + threadTimes.binderThreadCpuTimesUs = + new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000}; + + when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes); + + int transactionCode = 42; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1, + Binder.class, transactionCode, true /*screenInteractive */); + stat1.incrementalCallCount = 100; + stat1.recordedCallCount = 100; + stat1.cpuTimeMicros = 1_000_000; + callStats.add(stat1); + + mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2, + Binder.class, transactionCode, true /*screenInteractive */); + stat2.incrementalCallCount = 100; + stat2.recordedCallCount = 100; + stat2.cpuTimeMicros = 9_000_000; + callStats.add(stat2); + + mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats); + + mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc); + + mMockBatteryStats.prepareForDumpLocked(); } } diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index be45f1813a0e..7ad46a161283 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -975,7 +975,7 @@ public class RippleDrawable extends LayerDrawable { shader.setColor(color, effectColor); shader.setOrigin(cx, cy); shader.setTouch(x, y); - shader.setResolution(w, h, mState.mDensity); + shader.setResolution(w, h); shader.setNoisePhase(noisePhase); shader.setRadius(radius); shader.setProgress(.0f); @@ -1193,6 +1193,9 @@ public class RippleDrawable extends LayerDrawable { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); + if (mState.mRippleStyle == STYLE_PATTERNED) { + mRipplePaint.setDither(true); + } } final float x = mHotspotBounds.exactCenterX(); diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index eb726c1dad34..57b322334867 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -20,7 +20,6 @@ import android.annotation.ColorInt; import android.graphics.Color; import android.graphics.RuntimeShader; import android.graphics.Shader; -import android.util.DisplayMetrics; final class RippleShader extends RuntimeShader { private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n" @@ -204,8 +203,8 @@ final class RippleShader extends RuntimeShader { sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()}); } - public void setResolution(float w, float h, int density) { - final float densityScale = density * DisplayMetrics.DENSITY_DEFAULT_SCALE * 0.8f; + public void setResolution(float w, float h) { + final float densityScale = 2.1f; setUniform("in_resolutionScale", new float[] {1f / w, 1f / h}); setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h}); } diff --git a/location/java/android/location/LocationDeviceConfig.java b/location/java/android/location/LocationDeviceConfig.java index 92845745828b..c55eed9211f7 100644 --- a/location/java/android/location/LocationDeviceConfig.java +++ b/location/java/android/location/LocationDeviceConfig.java @@ -16,16 +16,11 @@ package android.location; -import android.annotation.SystemApi; -import android.annotation.TestApi; - /** * DeviceConfig keys within the location namespace. * * @hide */ -@SystemApi -@TestApi public final class LocationDeviceConfig { /** diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index f83dc407d870..ae44c5e34521 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1823,6 +1823,7 @@ public class LocationManager { * @deprecated Use {@link #isProviderPackage(String, String, String)} instead. * * @hide + * @removed */ @Deprecated @SystemApi diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 5bedd7a18890..9e39355e452d 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -73,11 +73,11 @@ public class BannerMessagePreference extends Preference { throw new IllegalArgumentException(); } - @ColorRes int getAccentColorResId() { + public @ColorRes int getAccentColorResId() { return mAccentColorResId; } - @ColorRes int getBackgroundColorResId() { + public @ColorRes int getBackgroundColorResId() { return mBackgroundColorResId; } } diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java index 4c45c5e3cb56..8c2621d3e4ec 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java @@ -16,6 +16,8 @@ package com.android.settingslib.collapsingtoolbar; +import static com.android.settingslib.transition.SettingsTransitionHelper.EXTRA_PAGE_TRANSITION_TYPE; + import android.app.ActivityOptions; import android.content.Intent; import android.os.Bundle; @@ -28,6 +30,7 @@ import androidx.core.os.BuildCompat; import androidx.fragment.app.FragmentActivity; import com.android.settingslib.transition.SettingsTransitionHelper; +import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType; /** * A base Activity for Settings-specific page transition. Activities extending it will get @@ -35,7 +38,6 @@ import com.android.settingslib.transition.SettingsTransitionHelper; */ public abstract class SettingsTransitionActivity extends FragmentActivity { private static final String TAG = "SettingsTransitionActivity"; - private static final int DEFAULT_REQUEST = -1; private Toolbar mToolbar; @@ -58,45 +60,17 @@ public abstract class SettingsTransitionActivity extends FragmentActivity { } @Override - public void startActivity(Intent intent) { - if (!isSettingsTransitionEnabled()) { - super.startActivity(intent); - return; - } - - super.startActivity(intent, createActivityOptionsBundleForTransition(null)); - } - - @Override - public void startActivity(Intent intent, @Nullable Bundle options) { - if (!isSettingsTransitionEnabled()) { - super.startActivity(intent, options); - return; - } - - super.startActivity(intent, createActivityOptionsBundleForTransition(options)); - } - - @Override - public void startActivityForResult(Intent intent, int requestCode) { - if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) { - super.startActivityForResult(intent, requestCode); - return; - } - - super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition( - null)); - } - - @Override public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) { + final int transitionType = intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, + TransitionType.TRANSITION_SHARED_AXIS); + if (!isSettingsTransitionEnabled() || + transitionType == TransitionType.TRANSITION_NONE) { super.startActivityForResult(intent, requestCode, options); return; } - super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition( - options)); + super.startActivityForResult(intent, requestCode, + createActivityOptionsBundleForTransition(options)); } protected boolean isSettingsTransitionEnabled() { diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java index 3d9396492643..4612861b5766 100644 --- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java +++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java @@ -55,6 +55,8 @@ public class SettingsTransitionHelper { int TRANSITION_FADE = 2; } + public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"; + private static final String TAG = "SettingsTransitionHelper"; private static final long DURATION = 450L; private static final float FADE_THROUGH_THRESHOLD = 0.22F; diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 404299a2bd38..af8cd1e49efe 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1133,7 +1133,7 @@ <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration --> <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string> <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited --> - <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Optimizing for battery health</string> + <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging temporarily limited</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index cf5408348bab..ae6165b80e74 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -169,7 +169,7 @@ public class SecureSettings { Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, Settings.Secure.PEOPLE_STRIP, Settings.Secure.MEDIA_CONTROLS_RESUME, - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 50fab4fb42e6..e09d4201e83e 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -251,8 +251,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME_BLOCKED, - COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); + VALIDATORS.put(Secure.MEDIA_CONTROLS_RECOMMENDATION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE, new InclusiveIntegerRangeValidator( Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index b3c437480af7..989010e8730c 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -114,6 +114,11 @@ public interface BcSmartspaceDataPlugin extends Plugin { * Set or clear next alarm information */ void setNextAlarm(@Nullable Drawable image, @Nullable String description); + + /** + * Set or clear device media playing + */ + void setMediaTarget(@Nullable SmartspaceTarget target); } /** Interface for launching Intents, which can differ on the lockscreen */ diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 4b6621379b44..caa216688cbc 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -79,8 +79,8 @@ is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. --> <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string> - <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's optimizing for battery health. --> - <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Optimizing for battery health</string> + <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's charging temporarily limited. --> + <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging temporarily limited</string> <!-- When the lock screen is showing and the battery is low, warn user to plug in the phone soon. --> diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index b08e51372dd0..f1cda277f045 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -126,6 +126,7 @@ android:gravity="right" android:layout_gravity="right" android:clipToPadding="false" + android:clipToOutline="true" android:background="@drawable/volume_row_rounded_background"> <com.android.systemui.volume.CaptionsToggleImageButton android:id="@+id/odi_captions_icon" @@ -146,6 +147,7 @@ android:layout="@layout/volume_tool_tip_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom | right"/> + android:layout_gravity="bottom | right" + android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml index e294dad6ce34..b7e86a3b0b84 100644 --- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml @@ -14,6 +14,7 @@ ~ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" @@ -52,7 +53,7 @@ android:paddingStart="8dp" android:paddingEnd="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="14sp" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml index 47cab423b0ad..1086a1361366 100644 --- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml @@ -16,6 +16,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:id="@+id/item" android:background="@drawable/people_space_tile_view_card" @@ -128,7 +129,7 @@ android:paddingStart="8dp" android:paddingEnd="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="14sp" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml index 7a1371d034b6..22fcd3bcce37 100644 --- a/packages/SystemUI/res/layout/people_tile_small.xml +++ b/packages/SystemUI/res/layout/people_tile_small.xml @@ -14,6 +14,7 @@ ~ limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -50,7 +51,7 @@ android:gravity="center" android:paddingHorizontal="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="@dimen/name_text_size_for_small" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 09d46856dec8..fb39d3e4027d 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -137,11 +137,12 @@ systemui:layout_constraintRight_toRightOf="parent" systemui:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="20dp" - android:paddingLeft="20dp" - android:paddingRight="20dp" - android:paddingTop="10dp" - android:paddingBottom="10dp" - android:elevation="10dp" + android:paddingHorizontal="16dp" + android:minHeight="44dp" + android:elevation="4dp" + android:background="@drawable/rounded_bg_full" + android:gravity="center" + android:text="@string/tap_again" android:visibility="gone" /> </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index beac05794fe9..51718d9af054 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -125,6 +125,7 @@ android:gravity="right" android:layout_gravity="right" android:clipToPadding="false" + android:clipToOutline="true" android:background="@drawable/volume_row_rounded_background"> <com.android.systemui.volume.CaptionsToggleImageButton android:id="@+id/odi_captions_icon" @@ -146,7 +147,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom | right" - android:layout_marginRight="@dimen/volume_tool_tip_right_margin" - android:layout_marginBottom="@dimen/volume_tool_tip_bottom_margin"/> + android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_tool_tip_view.xml b/packages/SystemUI/res/layout/volume_tool_tip_view.xml index 9fe885ebefc7..ee2496948752 100644 --- a/packages/SystemUI/res/layout/volume_tool_tip_view.xml +++ b/packages/SystemUI/res/layout/volume_tool_tip_view.xml @@ -17,6 +17,7 @@ <com.android.systemui.volume.VolumeToolTipView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/tooltip_view" android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -35,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:textColor="@android:color/white" + android:textColor="?android:attr/textColorPrimaryInverse" android:text="@string/volume_odi_captions_tip" android:textSize="14sp"/> <ImageView @@ -48,7 +49,7 @@ android:layout_marginEnd="2dp" android:alpha="0.7" android:src="@drawable/ic_remove_no_shadow" - android:tint="@android:color/white" + android:tint="?android:attr/textColorPrimaryInverse" android:background="?android:attr/selectableItemBackgroundBorderless" android:contentDescription="@string/accessibility_volume_close_odi_captions_tip"/> </LinearLayout> diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml index 220ed5cce83e..a9bc9e5cd638 100644 --- a/packages/SystemUI/res/values-land-television/dimens.xml +++ b/packages/SystemUI/res/values-land-television/dimens.xml @@ -23,6 +23,7 @@ <dimen name="volume_dialog_slider_width">4dp</dimen> <dimen name="volume_dialog_slider_corner_radius">@dimen/volume_dialog_slider_width</dimen> <dimen name="volume_dialog_background_blur_radius">100dp</dimen> + <dimen name="volume_tool_tip_right_margin">136dp</dimen> <dimen name="tv_volume_dialog_bubble_size">36dp</dimen> <dimen name="tv_volume_dialog_row_padding">6dp</dimen> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 215698dd1884..9df9db607b9f 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -43,7 +43,6 @@ <dimen name="qs_detail_margin_top">14dp</dimen> - <dimen name="volume_tool_tip_right_margin">136dp</dimen> <dimen name="volume_tool_tip_top_margin">12dp</dimen> <dimen name="volume_row_slider_height">128dp</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 6393147f8b0b..f4086edf627d 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -38,8 +38,6 @@ <!-- The new animations to/from lockscreen and AOD! --> <bool name="flag_lockscreen_animations">false</bool> - <bool name="flag_toast_style">false</bool> - <bool name="flag_pm_lite">false</bool> <bool name="flag_alarm_tile">false</bool> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 01d0dde6da5e..db3fae6fa491 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1062,6 +1062,9 @@ <!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] --> <string name="notification_tap_again">Tap again to open</string> + <!-- Asks for a second tap as confirmation on an item that normally requires one tap. [CHAR LIMIT=60] --> + <string name="tap_again">Tap again</string> + <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] --> <string name="keyguard_unlock">Swipe up to open</string> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index f98a959346d3..b2ae2a0ca8ad 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -40,7 +40,8 @@ android_library { name: "SystemUISharedLib", srcs: [ "src/**/*.java", - "src/**/I*.aidl", + "src/**/*.kt", + "src/**/*.aidl", ":wm_shell-aidls", ], diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 2cf3ad2dcd49..c468e416f8a5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -36,6 +36,9 @@ import java.util.StringJoiner; * Various shared constants between Launcher and SysUI as part of quickstep */ public class QuickStepContract { + // Fully qualified name of the Launcher activity. + public static final String LAUNCHER_ACTIVITY_CLASS_NAME = + "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; @@ -52,6 +55,8 @@ public class QuickStepContract { // See IStartingWindow.aidl public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = "extra_shell_starting_window"; + // See ISmartspaceTransitionController.aidl + public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition"; public static final String NAV_BAR_MODE_2BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl new file mode 100644 index 000000000000..511df4c285b4 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl @@ -0,0 +1,34 @@ +/* + * 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 com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.SmartspaceState; + +// Methods for getting and setting the state of a SmartSpace. This is used to allow a remote process +// (such as System UI) to sync with and control a SmartSpace view hosted in another process (such as +// Launcher). +interface ISmartspaceCallback { + + // Return information about the state of the SmartSpace, including location on-screen and + // currently selected page. + SmartspaceState getSmartspaceState(); + + // Set the currently selected page of this SmartSpace. + oneway void setSelectedPage(int selectedPage); + + oneway void setVisibility(int visibility); +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl new file mode 100644 index 000000000000..2b3e961ab3b2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl @@ -0,0 +1,24 @@ +/* + * 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 com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.ISmartspaceCallback; + +// Controller that keeps track of SmartSpace instances in remote processes (such as Launcher). +interface ISmartspaceTransitionController { + oneway void setSmartspace(ISmartspaceCallback callback); +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl new file mode 100644 index 000000000000..2d01d6af63c5 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl @@ -0,0 +1,21 @@ +/* + * 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 com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.SmartspaceState; + +parcelable SmartspaceState;
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt new file mode 100644 index 000000000000..2d51c4d13611 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace + +import android.graphics.Rect +import android.os.Parcel +import android.os.Parcelable + +/** + * Represents the state of a SmartSpace, including its location on screen and the index of the + * currently selected page. This object contains all of the information needed to synchronize two + * SmartSpace instances so that we can perform shared-element transitions between them. + */ +class SmartspaceState() : Parcelable { + var boundsOnScreen: Rect = Rect() + var selectedPage = 0 + + constructor(parcel: Parcel) : this() { + this.boundsOnScreen = parcel.readParcelable(Rect::javaClass.javaClass.classLoader) + this.selectedPage = parcel.readInt() + } + + override fun writeToParcel(dest: Parcel?, flags: Int) { + dest?.writeParcelable(boundsOnScreen, 0) + dest?.writeInt(selectedPage) + } + + override fun describeContents(): Int { + return 0 + } + + override fun toString(): String { + return "boundsOnScreen: $boundsOnScreen, selectedPage: $selectedPage" + } + + companion object CREATOR : Parcelable.Creator<SmartspaceState> { + override fun createFromParcel(parcel: Parcel): SmartspaceState { + return SmartspaceState(parcel) + } + + override fun newArray(size: Int): Array<SmartspaceState?> { + return arrayOfNulls(size) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index aa7f9a24b54a..28534d380c1f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -32,8 +32,10 @@ import com.android.keyguard.clock.ClockManager; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; @@ -44,7 +46,9 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.ViewController; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; import java.util.TimeZone; import javax.inject.Inject; @@ -92,6 +96,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS // If set, will replace keyguard_status_area private View mSmartspaceView; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private SmartspaceTransitionController mSmartspaceTransitionController; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -104,7 +111,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS BatteryController batteryController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController bypassController, - LockscreenSmartspaceController smartspaceController) { + LockscreenSmartspaceController smartspaceController, + KeyguardUnlockAnimationController keyguardUnlockAnimationController, + SmartspaceTransitionController smartspaceTransitionController) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; @@ -116,6 +125,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardUpdateMonitor = keyguardUpdateMonitor; mBypassController = bypassController; mSmartspaceController = smartspaceController; + + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mSmartspaceTransitionController = smartspaceTransitionController; } /** @@ -187,6 +199,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS nic.setLayoutParams(lp); mView.setSmartspaceView(mSmartspaceView); + mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView); } } @@ -285,12 +298,44 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mSmartspaceView != null) { PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X, x, props, animate); + + // If we're unlocking with the SmartSpace shared element transition, let the controller + // know that it should re-position our SmartSpace. + if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { + mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition(); + } else { + // Otherwise, reset Y translation in case it's still offset from a previous shared + // element transition. + ((View) mSmartspaceView).setTranslationY(0f); + } } mKeyguardSliceViewController.updatePosition(x, props, animate); mNotificationIconAreaController.updatePosition(x, props, animate); } + /** Sets an alpha value on every child view except for the smartspace. */ + public void setChildrenAlphaExcludingSmartspace(float alpha) { + final Set<View> excludedViews = new HashSet<>(); + + if (mSmartspaceView != null) { + excludedViews.add(mSmartspaceView); + } + + setChildrenAlphaExcluding(alpha, excludedViews); + } + + /** Sets an alpha value on every child view except for the views in the provided set. */ + public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) { + for (int i = 0; i < mView.getChildCount(); i++) { + final View child = mView.getChildAt(i); + + if (!excludedViews.contains(child)) { + child.setAlpha(alpha); + } + } + } + void updateTimeZone(TimeZone timeZone) { mView.onTimeZoneChanged(timeZone); if (mClockViewController != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 299ecebd021c..96eda3d95603 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -28,6 +28,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; +import android.view.ViewGroup; import android.widget.GridLayout; import android.widget.TextView; @@ -39,6 +40,7 @@ import com.android.systemui.statusbar.CrossFadeHelper; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Set; /** * View consisting of: @@ -55,6 +57,7 @@ public class KeyguardStatusView extends GridLayout { private final LockPatternUtils mLockPatternUtils; private final IActivityManager mIActivityManager; + private ViewGroup mStatusViewContainer; private TextView mLogoutView; private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; @@ -66,6 +69,7 @@ public class KeyguardStatusView extends GridLayout { private float mDarkAmount = 0; private int mTextColor; + private float mChildrenAlphaExcludingSmartSpace = 1f; /** * Bottom margin that defines the margin between bottom of smart space and top of notification @@ -132,6 +136,7 @@ public class KeyguardStatusView extends GridLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); + mStatusViewContainer = findViewById(R.id.status_view_container); mLogoutView = findViewById(R.id.logout); if (mLogoutView != null) { mLogoutView.setOnClickListener(this::onLogoutClicked); @@ -249,6 +254,27 @@ public class KeyguardStatusView extends GridLayout { mClockView.setTextColor(blendedTextColor); } + public void setChildrenAlphaExcludingClockView(float alpha) { + setChildrenAlphaExcluding(alpha, Set.of(mClockView)); + } + + /** Sets an alpha value on every view except for the views in the provided set. */ + public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) { + mChildrenAlphaExcludingSmartSpace = alpha; + + for (int i = 0; i < mStatusViewContainer.getChildCount(); i++) { + final View child = mStatusViewContainer.getChildAt(i); + + if (!excludedViews.contains(child)) { + child.setAlpha(alpha); + } + } + } + + public float getChildrenAlphaExcludingSmartSpace() { + return mChildrenAlphaExcludingSmartSpace; + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardStatusView:"); pw.println(" mOwnerInfo: " + (mOwnerInfo == null diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 388c085fd1a0..7b6514a63195 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -20,6 +20,8 @@ import android.graphics.Rect; import android.os.UserHandle; import android.util.Slog; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -49,6 +51,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final ConfigurationController mConfigurationController; private final DozeParameters mDozeParameters; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private final KeyguardStateController mKeyguardStateController; + private SmartspaceTransitionController mSmartspaceTransitionController; private final Rect mClipBounds = new Rect(); @Inject @@ -59,15 +64,33 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, - DozeParameters dozeParameters) { + DozeParameters dozeParameters, + KeyguardUnlockAnimationController keyguardUnlockAnimationController, + SmartspaceTransitionController smartspaceTransitionController) { super(keyguardStatusView); mKeyguardSliceViewController = keyguardSliceViewController; mKeyguardClockSwitchController = keyguardClockSwitchController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mConfigurationController = configurationController; mDozeParameters = dozeParameters; + mKeyguardStateController = keyguardStateController; mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters); + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mSmartspaceTransitionController = smartspaceTransitionController; + + mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + // If we explicitly re-show the keyguard, make sure that all the child views are + // visible. They might have been animating out as part of the SmartSpace shared + // element transition. + if (keyguardStateController.isShowing()) { + mView.setChildrenAlphaExcludingClockView(1f); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f); + } + } + }); } @Override @@ -137,7 +160,24 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV */ public void setAlpha(float alpha) { if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { - mView.setAlpha(alpha); + // If we're capable of performing the SmartSpace shared element transition, and we are + // going to (we're swiping to dismiss vs. bringing up the PIN screen), then fade out + // everything except for the SmartSpace. + if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { + mView.setChildrenAlphaExcludingClockView(alpha); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(alpha); + } else if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { + // Otherwise, we can just set the alpha for the entire container. + mView.setAlpha(alpha); + + // If we previously unlocked with the shared element transition, some child views + // might still have alpha = 0f. Set them back to 1f since we're just using the + // parent container's alpha. + if (mView.getChildrenAlphaExcludingSmartSpace() < 1f) { + mView.setChildrenAlphaExcludingClockView(1f); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index cc167b928c3c..e6fe0600b9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -133,6 +133,7 @@ public class SystemUIFactory { .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(Transitions.createEmptyForTesting()) .setStartingSurface(Optional.ofNullable(null)); + } mSysUIComponent = builder.build(); if (mInitializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 1396099db948..f422e9e40d33 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -45,6 +45,7 @@ import com.android.systemui.power.dagger.PowerModule; import com.android.systemui.recents.Recents; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.settings.dagger.SettingsModule; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -173,6 +174,12 @@ public abstract class SystemUIModule { return SystemUIFactory.getInstance(); } + @SysUISingleton + @Provides + static SmartspaceTransitionController provideSmartspaceTransitionController() { + return new SmartspaceTransitionController(); + } + // TODO: This should provided by the WM component /** Provides Optional of BubbleManager */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 411c328cd310..665376ac4569 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -23,11 +23,14 @@ import android.content.Context import android.graphics.Matrix import android.view.RemoteAnimationTarget import android.view.SyncRtSurfaceTransactionApplier +import android.view.View import androidx.core.math.MathUtils import com.android.internal.R +import com.android.keyguard.KeyguardClockSwitchController import com.android.keyguard.KeyguardViewController import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy import javax.inject.Inject @@ -85,7 +88,8 @@ class KeyguardUnlockAnimationController @Inject constructor( context: Context, private val keyguardStateController: KeyguardStateController, private val keyguardViewMediator: Lazy<KeyguardViewMediator>, - private val keyguardViewController: KeyguardViewController + private val keyguardViewController: KeyguardViewController, + private val smartspaceTransitionController: SmartspaceTransitionController ) : KeyguardStateController.Callback { /** @@ -131,6 +135,21 @@ class KeyguardUnlockAnimationController @Inject constructor( /** Rounded corner radius to apply to the surface behind the keyguard. */ private var roundedCornerRadius = 0f + /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */ + public var lockscreenSmartSpace: View? = null + + /** + * Whether we are currently in the process of unlocking the keyguard, and we are performing the + * shared element SmartSpace transition. + */ + private var unlockingWithSmartSpaceTransition: Boolean = false + + /** + * Whether we tried to start the SmartSpace shared element transition for this unlock swipe. + * It's possible we're unable to do so (if the Launcher SmartSpace is not available). + */ + private var attemptedSmartSpaceTransitionForThisSwipe = false + init { surfaceBehindAlphaAnimator.duration = 150 surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN @@ -214,6 +233,24 @@ class KeyguardUnlockAnimationController @Inject constructor( } /** + * Whether we are currently in the process of unlocking the keyguard, and we are performing the + * shared element SmartSpace transition. + */ + fun isUnlockingWithSmartSpaceTransition(): Boolean { + return unlockingWithSmartSpaceTransition + } + + /** + * Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As + * the dismiss amount increases, we will increase our SmartSpace's progress to the destination + * bounds (the location of the Launcher SmartSpace). + */ + fun updateLockscreenSmartSpacePosition() { + smartspaceTransitionController.setProgressToDestinationBounds( + keyguardStateController.dismissAmount / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD) + } + + /** * Scales in and translates up the surface behind the keyguard. This is used during unlock * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is * cancelled). @@ -284,36 +321,85 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + if (keyguardViewController.isShowing) { + updateKeyguardViewMediatorIfThresholdsReached() + + // If the surface is visible or it's about to be, start updating its appearance to + // reflect the new dismiss amount. + if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || + keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { + updateSurfaceBehindAppearAmount() + } + } + + // The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell + // Launcher's SmartSpace to become visible again), so update it even if the keyguard view is + // no longer showing. + updateSmartSpaceTransition() + } + + /** + * Lets the KeyguardViewMediator know if the dismiss amount has crossed a threshold of interest, + * such as reaching the point in the dismiss swipe where we need to make the surface behind the + * keyguard visible. + */ + private fun updateKeyguardViewMediatorIfThresholdsReached() { val dismissAmount = keyguardStateController.dismissAmount // Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have // crossed the threshold to finish the dismissal. val reachedHideKeyguardThreshold = (dismissAmount >= 1f || (keyguardStateController.isDismissingFromSwipe && - // Don't hide if we're flinging during a swipe, since we need to finish - // animating it out. This will be called again after the fling ends. - !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && - dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) + // Don't hide if we're flinging during a swipe, since we need to finish + // animating it out. This will be called again after the fling ends. + !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && + dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { - // We passed the threshold, and we're not yet showing the surface behind the keyguard. - // Animate it in. + // We passed the threshold, and we're not yet showing the surface behind the + // keyguard. Animate it in. keyguardViewMediator.get().showSurfaceBehindKeyguard() fadeInSurfaceBehind() } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { - // We're no longer past the threshold but we are showing the surface. Animate it out. + // We're no longer past the threshold but we are showing the surface. Animate it + // out. keyguardViewMediator.get().hideSurfaceBehindKeyguard() fadeOutSurfaceBehind() - } else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe && + } else if (keyguardViewMediator.get() + .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe && reachedHideKeyguardThreshold) { keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished() } + } - if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || - keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { - updateSurfaceBehindAppearAmount() + /** + * Updates flags related to the SmartSpace transition in response to a change in keyguard + * dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher + * know if it needs to do something as a result. + */ + private fun updateSmartSpaceTransition() { + val dismissAmount = keyguardStateController.dismissAmount + + // If we've begun a swipe, and are capable of doing the SmartSpace transition, start it! + if (!attemptedSmartSpaceTransitionForThisSwipe && + dismissAmount > 0f && + dismissAmount < 1f && + keyguardViewController.isShowing) { + attemptedSmartSpaceTransitionForThisSwipe = true + + smartspaceTransitionController.prepareForUnlockTransition() + if (keyguardStateController.canPerformSmartSpaceTransition()) { + unlockingWithSmartSpaceTransition = true + smartspaceTransitionController.launcherSmartspace?.setVisibility( + View.INVISIBLE) + } + } else if (attemptedSmartSpaceTransitionForThisSwipe && + (dismissAmount == 0f || dismissAmount == 1f)) { + attemptedSmartSpaceTransitionForThisSwipe = false + unlockingWithSmartSpaceTransition = false + smartspaceTransitionController.launcherSmartspace?.setVisibility(View.VISIBLE) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 8532a46f0fc5..27a4e93db4ad 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -447,11 +447,8 @@ public class MediaControlPanel { if (mKey != null) { closeGuts(); - mKeyguardDismissUtil.executeWhenUnlocked(() -> { - mMediaDataManagerLazy.get().dismissMediaData(mKey, - MediaViewController.GUTS_ANIMATION_DURATION + 100); - return true; - }, /* requiresShadeOpen */ true, false); + mMediaDataManagerLazy.get().dismissMediaData(mKey, + MediaViewController.GUTS_ANIMATION_DURATION + 100); } else { Log.w(TAG, "Dismiss media with null notification. Token uid=" + data.getToken().getUid()); @@ -576,11 +573,8 @@ public class MediaControlPanel { logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS /* isRecommendationCard */ true); closeGuts(); - mKeyguardDismissUtil.executeWhenUnlocked(() -> { - mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( - MediaViewController.GUTS_ANIMATION_DURATION + 100L); - return true; - }, true /* requiresShadeOpen */, false); + mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( + MediaViewController.GUTS_ANIMATION_DURATION + 100L); }); mController = null; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index ffcec2906167..c74f2fe64d6f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -138,17 +138,6 @@ class MediaDataManager( private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() // There should ONLY be at most one Smartspace media recommendation. private var smartspaceMediaTarget: SmartspaceTarget? = null - internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context) - set(value) { - // Update list - appsBlockedFromResume.clear() - appsBlockedFromResume.addAll(value) - - // Remove any existing resume players that are now blocked - appsBlockedFromResume.forEach { - removeAllForPackage(it) - } - } private var smartspaceSession: SmartspaceSession? = null @Inject @@ -690,7 +679,9 @@ class MediaDataManager( } override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) { - Log.d(TAG, "My Smartspace media updates are here") + if (!Utils.allowMediaRecommendations(context)) { + return + } val mediaTargets = targets.filterIsInstance<SmartspaceTarget>() when (mediaTargets.size) { 0 -> { @@ -736,8 +727,7 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) - if (useMediaResumption && removed?.resumeAction != null && - !isBlockedFromResume(removed.packageName) && removed?.isLocalSession == true) { + if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) { Log.d(TAG, "Not removing $key because resumable") // Move to resume key (aka package name) if that key doesn't already exist. val resumeAction = getResumeMediaAction(removed.resumeAction!!) @@ -765,13 +755,6 @@ class MediaDataManager( } } - private fun isBlockedFromResume(packageName: String?): Boolean { - if (packageName == null) { - return true - } - return appsBlockedFromResume.contains(packageName) - } - fun setMediaResumptionEnabled(isEnabled: Boolean) { if (useMediaResumption == isEnabled) { return @@ -844,7 +827,6 @@ class MediaDataManager( println("externalListeners: ${mediaDataFilter.listeners}") println("mediaEntries: $mediaEntries") println("useMediaResumption: $useMediaResumption") - println("appsBlockedFromResume: $appsBlockedFromResume") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index b0be57668d08..7fe408fb0cb7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -23,7 +23,6 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.media.MediaDescription -import android.media.session.MediaController import android.os.UserHandle import android.provider.Settings import android.service.media.MediaBrowserService @@ -59,7 +58,6 @@ class MediaResumeListener @Inject constructor( private var useMediaResumption: Boolean = Utils.useMediaResumption(context) private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue() - private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context) private lateinit var mediaDataManager: MediaDataManager @@ -124,14 +122,6 @@ class MediaResumeListener @Inject constructor( mediaDataManager.setMediaResumptionEnabled(useMediaResumption) } }, Settings.Secure.MEDIA_CONTROLS_RESUME) - - // Listen to changes in which apps are allowed to persist - tunerService.addTunable(object : TunerService.Tunable { - override fun onTuningChanged(key: String?, newValue: String?) { - blockedApps = Utils.getBlockedMediaApps(context) - mediaDataManager.appsBlockedFromResume = blockedApps - } - }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED) } private fun loadSavedComponents() { @@ -160,10 +150,8 @@ class MediaResumeListener @Inject constructor( } resumeComponents.forEach { - if (!blockedApps.contains(it.packageName)) { - val browser = mediaBrowserFactory.create(mediaBrowserCallback, it) - browser.findRecentMedia() - } + val browser = mediaBrowserFactory.create(mediaBrowserCallback, it) + browser.findRecentMedia() } } @@ -171,9 +159,9 @@ class MediaResumeListener @Inject constructor( if (useMediaResumption) { // If this had been started from a resume state, disconnect now that it's live mediaBrowser?.disconnect() + mediaBrowser = null // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume && - !blockedApps.contains(data.packageName) && data.isLocalSession) { + if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager @@ -201,7 +189,6 @@ class MediaResumeListener @Inject constructor( */ private fun tryUpdateResumptionList(key: String, componentName: ComponentName) { Log.d(TAG, "Testing if we can connect to $componentName") - mediaBrowser?.disconnect() mediaBrowser = mediaBrowserFactory.create( object : ResumeMediaBrowser.Callback() { override fun onConnected() { @@ -211,7 +198,6 @@ class MediaResumeListener @Inject constructor( override fun onError() { Log.e(TAG, "Cannot resume with $componentName") mediaDataManager.setResumeAction(key, null) - mediaBrowser?.disconnect() mediaBrowser = null } @@ -224,7 +210,6 @@ class MediaResumeListener @Inject constructor( Log.d(TAG, "Can get resumable media from $componentName") mediaDataManager.setResumeAction(key, getResumeAction(componentName)) updateResumptionList(componentName) - mediaBrowser?.disconnect() mediaBrowser = null } }, @@ -262,30 +247,7 @@ class MediaResumeListener @Inject constructor( */ private fun getResumeAction(componentName: ComponentName): Runnable { return Runnable { - mediaBrowser?.disconnect() - mediaBrowser = mediaBrowserFactory.create( - object : ResumeMediaBrowser.Callback() { - override fun onConnected() { - if (mediaBrowser?.token == null) { - Log.e(TAG, "Error after connect") - mediaBrowser?.disconnect() - mediaBrowser = null - return - } - Log.d(TAG, "Connected for restart $componentName") - val controller = MediaController(context, mediaBrowser!!.token) - val controls = controller.transportControls - controls.prepare() - controls.play() - } - - override fun onError() { - Log.e(TAG, "Resume failed for $componentName") - mediaBrowser?.disconnect() - mediaBrowser = null - } - }, - componentName) + mediaBrowser = mediaBrowserFactory.create(null, componentName) mediaBrowser?.restart() } } diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java index e453653cf7b5..fecc903326f5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java @@ -16,6 +16,7 @@ package com.android.systemui.media; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -47,7 +48,7 @@ public class ResumeMediaBrowser { private static final String TAG = "ResumeMediaBrowser"; private final Context mContext; - private final Callback mCallback; + @Nullable private final Callback mCallback; private MediaBrowserFactory mBrowserFactory; private MediaBrowser mMediaBrowser; private ComponentName mComponentName; @@ -58,8 +59,8 @@ public class ResumeMediaBrowser { * @param callback used to report media items found * @param componentName Component name of the MediaBrowserService this browser will connect to */ - public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName, - MediaBrowserFactory browserFactory) { + public ResumeMediaBrowser(Context context, @Nullable Callback callback, + ComponentName componentName, MediaBrowserFactory browserFactory) { mContext = context; mCallback = callback; mComponentName = componentName; @@ -93,18 +94,24 @@ public class ResumeMediaBrowser { List<MediaBrowser.MediaItem> children) { if (children.size() == 0) { Log.d(TAG, "No children found for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } } else { // We ask apps to return a playable item as the first child when sending // a request with EXTRA_RECENT; if they don't, no resume controls MediaBrowser.MediaItem child = children.get(0); MediaDescription desc = child.getDescription(); if (child.isPlayable() && mMediaBrowser != null) { - mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), - ResumeMediaBrowser.this); + if (mCallback != null) { + mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), + ResumeMediaBrowser.this); + } } else { Log.d(TAG, "Child found but not playable for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } } } disconnect(); @@ -113,7 +120,9 @@ public class ResumeMediaBrowser { @Override public void onError(String parentId) { Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } @@ -121,7 +130,9 @@ public class ResumeMediaBrowser { public void onError(String parentId, Bundle options) { Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId + ", options: " + options); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } }; @@ -138,13 +149,20 @@ public class ResumeMediaBrowser { Log.d(TAG, "Service connected for " + mComponentName); if (mMediaBrowser != null && mMediaBrowser.isConnected()) { String root = mMediaBrowser.getRoot(); - if (!TextUtils.isEmpty(root) && mMediaBrowser != null) { - mCallback.onConnected(); - mMediaBrowser.subscribe(root, mSubscriptionCallback); + if (!TextUtils.isEmpty(root)) { + if (mCallback != null) { + mCallback.onConnected(); + } + if (mMediaBrowser != null) { + mMediaBrowser.subscribe(root, mSubscriptionCallback); + } return; } } - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } /** @@ -153,7 +171,9 @@ public class ResumeMediaBrowser { @Override public void onConnectionSuspended() { Log.d(TAG, "Connection suspended for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } @@ -163,16 +183,18 @@ public class ResumeMediaBrowser { @Override public void onConnectionFailed() { Log.d(TAG, "Connection failed for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } }; /** - * Disconnect the media browser. This should be called after restart or testConnection have - * completed to close the connection. + * Disconnect the media browser. This should be done after callbacks have completed to + * disconnect from the media browser service. */ - public void disconnect() { + protected void disconnect() { if (mMediaBrowser != null) { mMediaBrowser.disconnect(); } @@ -183,7 +205,8 @@ public class ResumeMediaBrowser { * Connects to the MediaBrowserService and starts playback. * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called * depending on whether it was successful. - * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed. + * If the connection is successful, the listener should call ResumeMediaBrowser#disconnect after + * getting a media update from the app */ public void restart() { disconnect(); @@ -195,7 +218,10 @@ public class ResumeMediaBrowser { public void onConnected() { Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected()); if (mMediaBrowser == null || !mMediaBrowser.isConnected()) { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); return; } MediaSession.Token token = mMediaBrowser.getSessionToken(); @@ -203,17 +229,26 @@ public class ResumeMediaBrowser { controller.getTransportControls(); controller.getTransportControls().prepare(); controller.getTransportControls().play(); - mCallback.onConnected(); + if (mCallback != null) { + mCallback.onConnected(); + } + // listener should disconnect after media player update } @Override public void onConnectionFailed() { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } @Override public void onConnectionSuspended() { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } }, rootHints); mMediaBrowser.connect(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 9e77b60036a2..d8b342a6a8d8 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -31,6 +31,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; @@ -200,7 +201,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final UiEventLogger mUiEventLogger; private Bundle mSavedState; - private NavigationBarView mNavigationBarView = null; + private NavigationBarView mNavigationBarView; private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -392,7 +393,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, }; private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true); - + private final Runnable mEnableLayoutTransitions = () -> + mNavigationBarView.setLayoutTransitionsEnabled(true); private final Runnable mOnVariableDurationHomeLongClick = () -> { if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) { mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback( @@ -488,7 +490,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); } - public View getView() { + public NavigationBarView getView() { return mNavigationBarView; } @@ -504,16 +506,19 @@ public class NavigationBar implements View.OnAttachStateChangeListener, | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.token = new Binder(); - lp.setTitle("NavigationBar" + mContext.getDisplayId()); lp.accessibilityTitle = mContext.getString(R.string.nav_bar); - lp.windowAnimations = 0; lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + lp.windowAnimations = 0; + lp.setTitle("NavigationBar" + mContext.getDisplayId()); + lp.setFitInsetsTypes(0 /* types */); NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( R.layout.navigation_bar_window, null); View barView = LayoutInflater.from(frame.getContext()).inflate( R.layout.navigation_bar, frame); barView.addOnAttachStateChangeListener(this); + mNavigationBarView = barView.findViewById(R.id.navigation_bar_view); if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView); mContext.getSystemService(WindowManager.class).addView(frame, lp); @@ -533,6 +538,19 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED), false, mAssistContentObserver, UserHandle.USER_ALL); + mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean( + R.bool.allow_force_nav_bar_handle_opaque); + mForceNavBarHandleOpaque = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, + NAV_BAR_HANDLE_FORCE_OPAQUE, + /* defaultValue = */ true); + mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, + HOME_BUTTON_LONG_PRESS_DURATION_MS, + /* defaultValue = */ 0 + )).filter(duration -> duration != 0); + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); updateAssistantEntrypoints(); if (savedState != null) { @@ -543,25 +561,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false); } mSavedState = savedState; - mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); // Respect the latest disabled-flags. mCommandQueue.recomputeDisableFlags(mDisplayId, false); - mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean( - R.bool.allow_force_nav_bar_handle_opaque); - mForceNavBarHandleOpaque = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, - NAV_BAR_HANDLE_FORCE_OPAQUE, - /* defaultValue = */ true); - mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong( - DeviceConfig.NAMESPACE_SYSTEMUI, - HOME_BUTTON_LONG_PRESS_DURATION_MS, - /* defaultValue = */ 0 - )).filter(duration -> duration != 0); - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener); - mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); mDeviceProvisionedController.addCallback(mUserSetupListener); @@ -587,7 +590,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Override public void onViewAttachedToWindow(View v) { final Display display = v.getDisplay(); - mNavigationBarView = v.findViewById(R.id.navigation_bar_view); mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController()); mNavigationBarView.setDisabledFlags(mDisabledFlags1); mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged); @@ -598,6 +600,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.setNavigationIconHints(mNavigationIconHints); mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); mNavigationBarView.setBehavior(mBehavior); + + mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); + mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener); mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener); @@ -657,10 +662,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Override public void onViewDetachedFromWindow(View v) { - if (mNavigationBarView != null) { - mNavigationBarView.getBarTransitions().destroy(); - mNavigationBarView.getLightTransitionsController().destroy(mContext); - } + mNavigationBarView.getBarTransitions().destroy(); + mNavigationBarView.getLightTransitionsController().destroy(mContext); mOverviewProxyService.removeCallback(mOverviewProxyListener); mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); if (mOrientationHandle != null) { @@ -671,6 +674,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mOrientationHandleGlobalLayoutListener); } mHandler.removeCallbacks(mAutoDim); + mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); + mHandler.removeCallbacks(mEnableLayoutTransitions); mNavigationBarView = null; mOrientationHandle = null; } @@ -682,9 +687,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, outState.putInt(EXTRA_APPEARANCE, mAppearance); outState.putInt(EXTRA_BEHAVIOR, mBehavior); outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); - if (mNavigationBarView != null) { - mNavigationBarView.getLightTransitionsController().saveState(outState); - } + mNavigationBarView.getLightTransitionsController().saveState(outState); } /** @@ -809,15 +812,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, // mOrientedHandle is initialized lazily mOrientationHandle.setVisibility(View.GONE); } - if (mNavigationBarView != null) { - mNavigationBarView.setVisibility(View.VISIBLE); - mNavigationBarView.setOrientedHandleSamplingRegion(null); - } + mNavigationBarView.setVisibility(View.VISIBLE); + mNavigationBarView.setOrientedHandleSamplingRegion(null); } private void reconfigureHomeLongClick() { - if (mNavigationBarView == null - || mNavigationBarView.getHomeButton().getCurrentView() == null) { + if (mNavigationBarView.getHomeButton().getCurrentView() == null) { return; } if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) { @@ -844,17 +844,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs); pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled); pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled); - - if (mNavigationBarView != null) { - pw.println(" mNavigationBarWindowState=" - + windowStateToString(mNavigationBarWindowState)); - pw.println(" mNavigationBarMode=" - + BarTransitions.modeToString(mNavigationBarMode)); - dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); - mNavigationBarView.dump(pw); - } else { - pw.print(" mNavigationBarView=null"); - } + pw.println(" mNavigationBarWindowState=" + + windowStateToString(mNavigationBarWindowState)); + pw.println(" mNavigationBarMode=" + + BarTransitions.modeToString(mNavigationBarMode)); + dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); + mNavigationBarView.dump(pw); } // ----- CommandQueue Callbacks ----- @@ -889,10 +884,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, if (hints == mNavigationIconHints) return; mNavigationIconHints = hints; - - if (mNavigationBarView != null) { - mNavigationBarView.setNavigationIconHints(hints); - } + mNavigationBarView.setNavigationIconHints(hints); checkBarModes(); updateSystemUiStateFlags(-1); } @@ -911,23 +903,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, orientSecondaryHomeHandle(); } if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); - - if (mNavigationBarView != null) { - mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); - } + mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); } } @Override public void onRotationProposal(final int rotation, boolean isValid) { - if (mNavigationBarView == null) { - if (RotationContextButton.DEBUG_ROTATION) { - Log.v(TAG, "onRotationProposal proposedRotation=" + - Surface.rotationToString(rotation) + ", mNavigationBarView is null"); - } - return; - } - final int winRotation = mNavigationBarView.getDisplay().getRotation(); final boolean rotateSuggestionsDisabled = RotationButtonController .hasDisable2RotateSuggestionFlag(mDisabledFlags2); @@ -941,8 +922,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, + ", isValid=" + isValid + ", mNavBarWindowState=" + StatusBarManager.windowStateToString(mNavigationBarWindowState) + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled - + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null" - : rotationButton.isVisible())); + + ", isRotateButtonVisible=" + rotationButton.isVisible()); } // Respect the disabled flag, no need for action as flag change callback will handle hiding @@ -983,9 +963,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, boolean nbModeChanged = false; if (mAppearance != appearance) { mAppearance = appearance; - if (getView() == null) { - return; - } nbModeChanged = updateBarMode(barMode(mTransientShown, appearance)); } if (mLightBarController != null) { @@ -994,9 +971,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } if (mBehavior != behavior) { mBehavior = behavior; - if (mNavigationBarView != null) { - mNavigationBarView.setBehavior(behavior); - } + mNavigationBarView.setBehavior(behavior); updateSystemUiStateFlags(-1); } } @@ -1034,12 +1009,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } private void handleTransientChanged() { - if (getView() == null) { - return; - } - if (mNavigationBarView != null) { - mNavigationBarView.onTransientStateChanged(mTransientShown); - } + mNavigationBarView.onTransientStateChanged(mTransientShown); final int barMode = barMode(mTransientShown, mAppearance); if (updateBarMode(barMode) && mLightBarController != null) { mLightBarController.onNavigationBarModeChanged(barMode); @@ -1092,9 +1062,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, | StatusBarManager.DISABLE_SEARCH); if (masked != mDisabledFlags1) { mDisabledFlags1 = masked; - if (mNavigationBarView != null) { - mNavigationBarView.setDisabledFlags(state1); - } + mNavigationBarView.setDisabledFlags(state1); updateScreenPinningGestures(); } @@ -1110,17 +1078,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private void setDisabled2Flags(int state2) { // Method only called on change of disable2 flags - if (mNavigationBarView != null) { - mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2); - } + mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2); } // ----- Internal stuff ----- private void refreshLayout(int layoutDirection) { - if (mNavigationBarView != null) { - mNavigationBarView.setLayoutDirection(layoutDirection); - } + mNavigationBarView.setLayoutDirection(layoutDirection); } private boolean shouldDisableNavbarGestures() { @@ -1129,7 +1093,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } private void repositionNavigationBar() { - if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; + if (!mNavigationBarView.isAttachedToWindow()) return; prepareNavigationBarView(); @@ -1138,10 +1102,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } private void updateScreenPinningGestures() { - if (mNavigationBarView == null) { - return; - } - // Change the cancel pin gesture to home and back if recents button is invisible boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); ButtonDispatcher backButton = mNavigationBarView.getBackButton(); @@ -1252,10 +1212,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); mAssistManagerLazy.get().startAssist(args); mStatusBarLazy.get().awakenDreams(); - - if (mNavigationBarView != null) { - mNavigationBarView.abortCurrentGesture(); - } + mNavigationBarView.abortCurrentGesture(); return true; } @@ -1405,9 +1362,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { - if (mNavigationBarView == null) { - return; - } boolean[] feedbackEnabled = new boolean[1]; int a11yFlags = getA11yButtonState(feedbackEnabled); @@ -1593,7 +1547,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, public void disableAnimationsDuringHide(long delay) { mNavigationBarView.setLayoutTransitionsEnabled(false); - mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true), + mHandler.postDelayed(mEnableLayoutTransitions, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); } @@ -1608,10 +1562,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } public NavigationBarTransitions getBarTransitions() { - if (mNavigationBarView != null) { - return mNavigationBarView.getBarTransitions(); - } - return null; + return mNavigationBarView.getBarTransitions(); } public void finishBarAnimations() { @@ -1626,8 +1577,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } private final Consumer<Integer> mRotationWatcher = rotation -> { - if (mNavigationBarView != null - && mNavigationBarView.needsReorient(rotation)) { + if (mNavigationBarView.needsReorient(rotation)) { repositionNavigationBar(); } }; @@ -1635,9 +1585,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (mNavigationBarView == null) { - return; - } String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index ae9b5987bac5..8b5a537ba242 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -123,8 +123,7 @@ public class NavigationBarController implements Callbacks, // Tracks config changes that will actually recreate the nav bar private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( - ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_SCREEN_LAYOUT + ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE); @Inject @@ -225,10 +224,7 @@ public class NavigationBarController implements Callbacks, if (navBar == null) { continue; } - NavigationBarView view = (NavigationBarView) navBar.getView(); - if (view != null) { - view.updateStates(); - } + navBar.getView().updateStates(); } }); } @@ -366,13 +362,12 @@ public class NavigationBarController implements Callbacks, mHandler, mNavBarOverlayController, mUiEventLogger); + mNavigationBars.put(displayId, navBar); View navigationBarView = navBar.createView(savedState); navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { - mNavigationBars.put(displayId, navBar); - if (result != null) { navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken, result.mImeWindowVis, result.mImeBackDisposition, @@ -448,7 +443,7 @@ public class NavigationBarController implements Callbacks, */ public @Nullable NavigationBarView getNavigationBarView(int displayId) { NavigationBar navBar = mNavigationBars.get(displayId); - return (navBar == null) ? null : (NavigationBarView) navBar.getView(); + return (navBar == null) ? null : navBar.getView(); } /** @return {@link NavigationBar} on the default display. */ diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java index 4d250797f944..02180160fc11 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java @@ -40,7 +40,6 @@ public class ButtonDispatcher { private final ArrayList<View> mViews = new ArrayList<>(); private final int mId; - private final AssistManager mAssistManager; private View.OnClickListener mClickListener; private View.OnTouchListener mTouchListener; @@ -73,7 +72,6 @@ public class ButtonDispatcher { public ButtonDispatcher(int id) { mId = id; - mAssistManager = Dependency.get(AssistManager.class); } public void clear() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index be7adbc77ca1..74d3425edd22 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -430,12 +430,14 @@ public class InternetTile extends QSTileImpl<SignalState> { } else { state.icon = ResourceIcon.get(cb.mWifiSignalIconId); } - } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); - } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } else if (cb.mNoDefaultNetwork) { + if (cb.mNoNetworksAvailable) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } } else if (cb.mIsTransient) { state.icon = ResourceIcon.get( com.android.internal.R.drawable.ic_signal_wifi_transient_animation); @@ -488,12 +490,14 @@ public class InternetTile extends QSTileImpl<SignalState> { state.state = Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.status_bar_airplane); - } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); - } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } else if (cb.mNoDefaultNetwork) { + if (cb.mNoNetworksAvailable) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } } else { state.icon = new SignalIcon(cb.mMobileSignalIconId); state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index e46792555101..64aec5e0b32b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -18,13 +18,14 @@ package com.android.systemui.qs.tiles; import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; + import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.service.quickaccesswallet.GetWalletCardsError; -import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.WalletCard; @@ -51,6 +52,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.systemui.wallet.ui.WalletActivity; import java.util.List; @@ -66,16 +68,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { private final CharSequence mLabel = mContext.getString(R.string.wallet_title); private final WalletCardRetriever mCardRetriever = new WalletCardRetriever(); - // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes. - private final QuickAccessWalletClient mQuickAccessWalletClient; private final KeyguardStateController mKeyguardStateController; private final PackageManager mPackageManager; private final SecureSettings mSecureSettings; private final Executor mExecutor; + private final QuickAccessWalletController mController; private final FeatureFlags mFeatureFlags; - @VisibleForTesting Drawable mCardViewDrawable; private WalletCard mSelectedCard; + @VisibleForTesting Drawable mCardViewDrawable; @Inject public QuickAccessWalletTile( @@ -87,15 +88,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - QuickAccessWalletClient quickAccessWalletClient, KeyguardStateController keyguardStateController, PackageManager packageManager, SecureSettings secureSettings, - @Background Executor executor, + @Main Executor executor, + QuickAccessWalletController quickAccessWalletController, FeatureFlags featureFlags) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); - mQuickAccessWalletClient = quickAccessWalletClient; + mController = quickAccessWalletController; mKeyguardStateController = keyguardStateController; mPackageManager = packageManager; mSecureSettings = secureSettings; @@ -115,7 +116,11 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { protected void handleSetListening(boolean listening) { super.handleSetListening(listening); if (listening) { - queryWalletCards(); + mController.setupWalletChangeObservers(mCardRetriever, DEFAULT_PAYMENT_APP_CHANGE); + if (!mController.getWalletClient().isWalletServiceAvailable()) { + mController.reCreateWalletClient(); + } + mController.queryWalletCards(mCardRetriever); } } @@ -139,12 +144,13 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { mContext.startActivity(intent); } } else { - if (mQuickAccessWalletClient.createWalletIntent() == null) { + if (mController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0, + mController.getWalletClient().createWalletIntent(), + /* delay= */ 0, animationController); } }); @@ -152,30 +158,34 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override protected void handleUpdateState(State state, Object arg) { - CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + CharSequence label = mController.getWalletClient().getServiceLabel(); state.label = label == null ? mLabel : label; state.contentDescription = state.label; state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); - if (mQuickAccessWalletClient.isWalletServiceAvailable()) { + if (mController.getWalletClient().isWalletServiceAvailable()) { if (mSelectedCard != null) { if (isDeviceLocked) { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_device_locked); + state.sideViewCustomDrawable = null; } else { state.state = Tile.STATE_ACTIVE; state.secondaryLabel = mSelectedCard.getContentDescription(); + state.sideViewCustomDrawable = mCardViewDrawable; } } else { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_no_card); + state.sideViewCustomDrawable = null; } state.stateDescription = state.secondaryLabel; } else { state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = null; + state.sideViewCustomDrawable = null; } - state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable; } @Override @@ -198,19 +208,14 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public CharSequence getTileLabel() { - CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + CharSequence label = mController.getWalletClient().getServiceLabel(); return label == null ? mLabel : label; } - private void queryWalletCards() { - int cardWidth = - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); - int cardHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); - int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); - GetWalletCardsRequest request = - new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); - mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever); + @Override + protected void handleDestroy() { + super.handleDestroy(); + mController.unregisterWalletChangeObservers(DEFAULT_PAYMENT_APP_CHANGE); } private class WalletCardRetriever implements diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 89cb5af58928..f4c15fbf90b8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHE import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; @@ -88,6 +89,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; @@ -148,6 +150,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final CommandQueue mCommandQueue; private final ShellTransitions mShellTransitions; private final Optional<StartingSurface> mStartingSurface; + private final SmartspaceTransitionController mSmartspaceTransitionController; private Region mActiveNavBarRegion; @@ -552,6 +555,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mStartingSurface.ifPresent((startingwindow) -> params.putBinder( KEY_EXTRA_SHELL_STARTING_WINDOW, startingwindow.createExternalInterface().asBinder())); + params.putBinder( + KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER, + mSmartspaceTransitionController.createExternalInterface().asBinder()); try { Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser=" @@ -613,7 +619,8 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, - Optional<StartingSurface> startingSurface) { + Optional<StartingSurface> startingSurface, + SmartspaceTransitionController smartspaceTransitionController) { super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; @@ -675,6 +682,7 @@ public class OverviewProxyService extends CurrentUserTracker implements updateEnabledState(); startConnectionToCurrentUser(); mStartingSurface = startingSurface; + mSmartspaceTransitionController = smartspaceTransitionController; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt new file mode 100644 index 000000000000..89b3df0f495f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt @@ -0,0 +1,143 @@ +/* + * 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 com.android.systemui.shared.system.smartspace + +import android.graphics.Rect +import android.view.View +import com.android.systemui.shared.system.ActivityManagerWrapper +import com.android.systemui.shared.system.QuickStepContract +import kotlin.math.min + +/** + * Controller that keeps track of SmartSpace instances in remote processes (such as Launcher), + * allowing System UI to query or update their state during shared-element transitions. + */ +class SmartspaceTransitionController { + + /** + * Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it + * to provide us with a callback to query and update the state of its Smartspace. + */ + private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() { + override fun setSmartspace(callback: ISmartspaceCallback?) { + this@SmartspaceTransitionController.launcherSmartspace = callback + updateLauncherSmartSpaceState() + } + } + + /** + * Callback provided by Launcher to allow us to query and update the state of its SmartSpace. + */ + public var launcherSmartspace: ISmartspaceCallback? = null + + public var lockscreenSmartspace: View? = null + + /** + * Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid + * unnecessary + */ + public var mLauncherSmartspaceState: SmartspaceState? = null + + /** + * The bounds of our SmartSpace when the shared element transition began. We'll interpolate + * between this and [smartspaceDestinationBounds] as the dismiss amount changes. + */ + private val smartspaceOriginBounds = Rect() + + /** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */ + + private val smartspaceDestinationBounds = Rect() + + fun createExternalInterface(): ISmartspaceTransitionController { + return ISmartspaceTransitionController + } + + /** + * Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the + * cached [mLauncherSmartspaceState] if possible. + */ + fun updateLauncherSmartSpaceState(): SmartspaceState? { + return launcherSmartspace?.smartspaceState.also { + mLauncherSmartspaceState = it + } + } + + fun prepareForUnlockTransition() { + updateLauncherSmartSpaceState().also { state -> + if (state?.boundsOnScreen != null && lockscreenSmartspace != null) { + lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds) + with(smartspaceDestinationBounds) { + set(state.boundsOnScreen) + offset(-lockscreenSmartspace!!.paddingLeft, + -lockscreenSmartspace!!.paddingTop) + } + } + } + } + + fun setProgressToDestinationBounds(progress: Float) { + if (!isSmartspaceTransitionPossible()) { + return + } + + val progressClamped = min(1f, progress) + + // Calculate the distance (relative to the origin) that we need to be for the current + // progress value. + val progressX = + (smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped + val progressY = + (smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped + + val lockscreenSmartspaceCurrentBounds = Rect().also { + lockscreenSmartspace!!.getBoundsOnScreen(it) + } + + // Figure out how far that is from our present location on the screen. This approach + // compensates for the fact that our parent container is also translating to animate out. + val dx = smartspaceOriginBounds.left + progressX - + lockscreenSmartspaceCurrentBounds.left + var dy = smartspaceOriginBounds.top + progressY - + lockscreenSmartspaceCurrentBounds.top + + with(lockscreenSmartspace!!) { + translationX = translationX + dx + translationY = translationY + dy + } + } + + /** + * Whether we're capable of performing the Smartspace shared element transition when we unlock. + * This is true if: + * + * - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen. + * - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected + * page. + */ + public fun isSmartspaceTransitionPossible(): Boolean { + val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true + return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty + } + + companion object { + fun isLauncherUnderneath(): Boolean { + return ActivityManagerWrapper.getInstance() + .runningTask?.topActivity?.className?.equals( + QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index ac6d9e8820ad..c7b6e67cf96d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -61,10 +61,6 @@ public class FeatureFlags { return mFlagReader.isEnabled(R.bool.flag_conversations); } - public boolean isToastStyleEnabled() { - return mFlagReader.isEnabled(R.bool.flag_toast_style); - } - public boolean isMonetEnabled() { return mFlagReader.isEnabled(R.bool.flag_monet); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index b33424c563a3..acfd998ff6e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -35,7 +35,6 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener; -import java.util.List; /** * The view in the statusBar that contains part of the heads-up information @@ -55,10 +54,8 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { private int[] mTmpPosition = new int[2]; private boolean mFirstLayout = true; private int mMaxWidth; - private View mRootView; private int mSysWinInset; private int mCutOutInset; - private List<Rect> mCutOutBounds; private Rect mIconDrawingRect = new Rect(); private Point mDisplaySize; private Runnable mOnDrawingRectChangedListener; @@ -197,19 +194,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset; boolean isRtl = isLayoutRtl(); int start = isRtl ? (mDisplaySize.x - right) : left; - if (start != targetPadding) { - if (mCutOutBounds != null) { - for (Rect cutOutRect : mCutOutBounds) { - int cutOutStart = (isRtl) - ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left; - if (start > cutOutStart) { - start -= cutOutRect.width(); - break; - } - } - } - int newPadding = targetPadding - start + getPaddingStart(); setPaddingRelative(newPadding, 0, mEndMargin, 0); } @@ -252,12 +237,6 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { getDisplaySize(); - mCutOutBounds = null; - if (displayCutout != null && displayCutout.getSafeInsetRight() == 0 - && displayCutout.getSafeInsetLeft() == 0) { - mCutOutBounds = displayCutout.getBoundingRects(); - } - // For Double Cut Out mode, the System window navigation bar is at the right // side of the left cut out. In this condition, mSysWinInset include the left cut // out width so we set mCutOutInset to be 0. For RTL, the condition is the same. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java index b6357b8dbd8d..045a1976d502 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -36,6 +36,12 @@ public interface NotificationShadeWindowController extends RemoteInputController */ default void registerCallback(StatusBarWindowCallback callback) {} + /** + * Unregisters a {@link StatusBarWindowCallback previous registered with + * {@link #registerCallback(StatusBarWindowCallback)}} + */ + default void unregisterCallback(StatusBarWindowCallback callback) {} + /** Notifies the registered {@link StatusBarWindowCallback} instances. */ default void notifyStateChangedCallbacks() {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 889dfde3a66d..9dc4ac952b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -358,9 +358,13 @@ public class NotificationShelf extends ActivatableNotificationView implements && anv instanceof ExpandableNotificationRow && ((ExpandableNotificationRow) anv).isHeadsUp(); + final boolean isHunGoingToShade = mAmbientState.isShadeExpanded() + && anv == mAmbientState.getTrackedHeadsUpRow(); + final boolean shouldUpdateCornerRoundness = viewStart < shelfStart && !mHostLayoutController.isViewAffectedBySwipe(anv) && !isUnlockedHeadsUp + && !isHunGoingToShade && !mAmbientState.isPulsing() && !mAmbientState.isDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 655ed4132726..33aa7c720b22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -26,14 +26,18 @@ import android.os.Process import android.provider.DeviceConfig import android.util.Log import android.view.View +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.phone.StatusBarWindowController import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.util.Assert import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock +import java.io.FileDescriptor +import java.io.PrintWriter import javax.inject.Inject @@ -59,9 +63,10 @@ class SystemStatusAnimationScheduler @Inject constructor( private val coordinator: SystemEventCoordinator, private val chipAnimationController: SystemEventChipAnimationController, private val statusBarWindowController: StatusBarWindowController, + private val dumpManager: DumpManager, private val systemClock: SystemClock, @Main private val executor: DelayableExecutor -) : CallbackController<SystemStatusAnimationCallback> { +) : CallbackController<SystemStatusAnimationCallback>, Dumpable { companion object { private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator" @@ -71,10 +76,6 @@ class SystemStatusAnimationScheduler @Inject constructor( PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true) } - /** True from the time a scheduled event starts until it's animation finishes */ - var isActive = false - private set - @SystemAnimationState var animationState: Int = IDLE private set @@ -88,6 +89,7 @@ class SystemStatusAnimationScheduler @Inject constructor( init { coordinator.attachScheduler(this) + dumpManager.registerDumpable(TAG, this) } fun onStatusEvent(event: StatusEvent) { @@ -293,6 +295,20 @@ class SystemStatusAnimationScheduler @Inject constructor( anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState) } + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("Scheduled event: $scheduledEvent") + pw.println("Has persistent privacy dot: $hasPersistentDot") + pw.println("Animation state: $animationState") + pw.println("Listeners:") + if (listeners.isEmpty()) { + pw.println("(none)") + } else { + listeners.forEach { + pw.println(" $it") + } + } + } + inner class ChipAnimatorAdapter( @SystemAnimationState val endState: Int, val viewCreator: (context: Context) -> View diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index d9cbfda8b3c8..86465b6f6b1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -380,11 +380,15 @@ public class StackScrollAlgorithm { ExpandableViewState viewState = view.getViewState(); viewState.location = ExpandableViewState.LOCATION_UNKNOWN; - if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) { - viewState.alpha = Interpolators.getNotificationScrimAlpha( - ambientState.getExpansionFraction(), true /* notification */); - } else { - viewState.alpha = 1f - ambientState.getHideAmount(); + final boolean isHunGoingToShade = ambientState.isShadeExpanded() + && view == ambientState.getTrackedHeadsUpRow(); + if (!isHunGoingToShade) { + if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) { + viewState.alpha = Interpolators.getNotificationScrimAlpha( + ambientState.getExpansionFraction(), true /* notification */); + } else { + viewState.alpha = 1f - ambientState.getHideAmount(); + } } if (view.mustStayOnScreen() && viewState.yTranslation >= 0) { @@ -414,13 +418,15 @@ public class StackScrollAlgorithm { } if (view instanceof FooterView) { + final boolean shadeClosed = !ambientState.isShadeExpanded(); final boolean isShelfShowing = algorithmState.firstViewInShelf != null; final float footerEnd = algorithmState.mCurrentExpandedYPosition + view.getIntrinsicHeight(); final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight(); - viewState.hidden = isShelfShowing || noSpaceForFooter; + viewState.hidden = shadeClosed || isShelfShowing || noSpaceForFooter; + } else if (view != ambientState.getTrackedHeadsUpRow()) { if (ambientState.isExpansionChanging()) { // Show all views. Views below the shelf will later be clipped (essentially hidden) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 7f919b5f5cf5..4d8e7de37606 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -24,6 +24,8 @@ import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTT import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -39,7 +41,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; @@ -49,13 +50,10 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; -import android.provider.Settings; import android.service.media.CameraPrewarmService; import android.service.quickaccesswallet.GetWalletCardsError; -import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.QuickAccessWalletClient; -import android.service.quickaccesswallet.QuickAccessWalletClientImpl; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.AttributeSet; @@ -71,7 +69,6 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; @@ -97,11 +94,9 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PreviewInflater; import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory; import com.android.systemui.tuner.TunerService; -import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.systemui.wallet.ui.WalletActivity; -import java.util.concurrent.Executor; - /** * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status * text. @@ -137,10 +132,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mLeftAffordanceView; private ImageView mWalletButton; - private boolean mWalletEnabled = false; private boolean mHasCard = false; private WalletCardRetriever mCardRetriever = new WalletCardRetriever(); - private QuickAccessWalletClient mQuickAccessWalletClient; + private QuickAccessWalletController mQuickAccessWalletController; private ViewGroup mIndicationArea; private TextView mIndicationText; @@ -159,7 +153,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private StatusBar mStatusBar; private KeyguardAffordanceHelper mAffordanceHelper; private FalsingManager mFalsingManager; - @Nullable private Executor mUiExecutor; private boolean mUserSetupComplete; private boolean mPrewarmBound; private Messenger mPrewarmMessenger; @@ -193,8 +186,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private int mBurnInYOffset; private ActivityIntentHelper mActivityIntentHelper; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private ContentObserver mWalletPreferenceObserver; - private SecureSettings mSecureSettings; public KeyguardBottomAreaView(Context context) { this(context, null); @@ -332,8 +323,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL getContext().unregisterReceiver(mDevicePolicyReceiver); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - if (mWalletPreferenceObserver != null) { - mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver); + if (mQuickAccessWalletController != null) { + mQuickAccessWalletController.unregisterWalletChangeObservers( + WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); } } @@ -456,7 +448,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateWalletVisibility() { - if (mDozing || !mWalletEnabled || !mHasCard) { + if (mDozing + || mQuickAccessWalletController == null + || !mQuickAccessWalletController.isWalletEnabled() + || !mHasCard) { mWalletButton.setVisibility(GONE); mIndicationArea.setPadding(0, 0, 0, 0); } else { @@ -690,7 +685,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onKeyguardShowingChanged() { if (mKeyguardStateController.isShowing()) { - queryWalletCards(); + if (mQuickAccessWalletController != null) { + mQuickAccessWalletController.queryWalletCards(mCardRetriever); + } } } @@ -935,50 +932,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL /** * Initialize the wallet feature, only enabling if the feature is enabled within the platform. */ - public void initWallet(QuickAccessWalletClient client, Executor uiExecutor, - SecureSettings secureSettings) { - mQuickAccessWalletClient = client; - mSecureSettings = secureSettings; - setupWalletPreferenceObserver(); - updateWalletPreference(); - - mUiExecutor = uiExecutor; - queryWalletCards(); + public void initWallet( + QuickAccessWalletController controller) { + mQuickAccessWalletController = controller; + mQuickAccessWalletController.setupWalletChangeObservers( + mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); + mQuickAccessWalletController.updateWalletPreference(); + mQuickAccessWalletController.queryWalletCards(mCardRetriever); updateWalletVisibility(); } - private void setupWalletPreferenceObserver() { - if (mWalletPreferenceObserver == null) { - mWalletPreferenceObserver = new ContentObserver(null /* handler */) { - @Override - public void onChange(boolean selfChange) { - mUiExecutor.execute(() -> updateWalletPreference()); - } - }; - - mSecureSettings.registerContentObserver( - Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY), - false /* notifyForDescendants */, - mWalletPreferenceObserver); - } - } - - private void updateWalletPreference() { - mWalletEnabled = mQuickAccessWalletClient.isWalletFeatureAvailable() - && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked(); - } - - private void queryWalletCards() { - if (!mWalletEnabled || mUiExecutor == null) { - return; - } - GetWalletCardsRequest request = - new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */, - 1 /* iconSizePx */, 1 /* maxCards */); - mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever); - } - private void onWalletClick(View v) { // More coming here; need to inform the user about how to proceed if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { @@ -991,12 +955,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } else { - if (mQuickAccessWalletClient.createWalletIntent() == null) { + if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0); + mQuickAccessWalletController.getWalletClient().createWalletIntent(), + /* delay= */ 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 0c4bec2c2c65..eef24200a882 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -116,6 +116,12 @@ public class KeyguardStatusBarView extends RelativeLayout implements // right and left padding applied to this view to account for cutouts and rounded corners private Pair<Integer, Integer> mPadding = new Pair(0, 0); + /** + * The clipping on the top + */ + private int mTopClipping; + private final Rect mClipRect = new Rect(0, 0, 0, 0); + public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); mUserManager = UserManager.get(getContext()); @@ -549,4 +555,25 @@ public class KeyguardStatusBarView extends RelativeLayout implements public void onSystemChromeAnimationUpdate(ValueAnimator anim) { mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue()); } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + updateClipping(); + } + + /** + * Set the clipping on the top of the view. + */ + public void setTopClipping(int topClipping) { + if (topClipping != mTopClipping) { + mTopClipping = topClipping; + updateClipping(); + } + } + + private void updateClipping() { + mClipRect.set(0, mTopClipping, getWidth(), getHeight()); + setClipBounds(mClipRect); + } } 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 74071439b0d7..7737420cf026 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -27,7 +27,6 @@ import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; -import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade; import static java.lang.Float.isNaN; @@ -54,7 +53,6 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.UserManager; import android.os.VibrationEffect; -import android.service.quickaccesswallet.QuickAccessWalletClient; import android.util.Log; import android.util.MathUtils; import android.view.DisplayCutout; @@ -153,6 +151,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.Utils; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.FileDescriptor; @@ -310,6 +309,7 @@ public class NotificationPanelViewController extends PanelViewController { private final FeatureFlags mFeatureFlags; private final ScrimController mScrimController; private final PrivacyDotViewController mPrivacyDotViewController; + private final QuickAccessWalletController mQuickAccessWalletController; // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications @@ -582,8 +582,8 @@ public class NotificationPanelViewController extends PanelViewController { private int mScrimCornerRadius; private int mScreenCornerRadius; private int mNotificationScrimPadding; + private boolean mQSAnimatingHiddenFromCollapsed; - private final QuickAccessWalletClient mQuickAccessWalletClient; private final Executor mUiExecutor; private final SecureSettings mSecureSettings; @@ -664,11 +664,11 @@ public class NotificationPanelViewController extends PanelViewController { AmbientState ambientState, LockIconViewController lockIconViewController, FeatureFlags featureFlags, - QuickAccessWalletClient quickAccessWalletClient, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, TapAgainViewController tapAgainViewController, FragmentService fragmentService, + QuickAccessWalletController quickAccessWalletController, @Main Executor uiExecutor, SecureSettings secureSettings) { super(view, falsingManager, dozeLog, keyguardStateController, @@ -679,6 +679,7 @@ public class NotificationPanelViewController extends PanelViewController { mVibratorHelper = vibratorHelper; mKeyguardMediaController = keyguardMediaController; mPrivacyDotViewController = privacyDotViewController; + mQuickAccessWalletController = quickAccessWalletController; mMetricsLogger = metricsLogger; mActivityManager = activityManager; mConfigurationController = configurationController; @@ -721,7 +722,6 @@ public class NotificationPanelViewController extends PanelViewController { mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); mUserManager = userManager; mMediaDataManager = mediaDataManager; - mQuickAccessWalletClient = quickAccessWalletClient; mTapAgainViewController = tapAgainViewController; mUiExecutor = uiExecutor; mSecureSettings = secureSettings; @@ -1122,7 +1122,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea.setFalsingManager(mFalsingManager); if (mFeatureFlags.isQuickAccessWalletEnabled()) { - mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor, mSecureSettings); + mKeyguardBottomArea.initWallet(mQuickAccessWalletController); } } @@ -1259,7 +1259,7 @@ public class NotificationPanelViewController extends PanelViewController { bypassEnabled, getUnlockedStackScrollerPadding(), computeQsExpansionFraction(), mDisplayCutoutTopInset, - shouldUseSplitNotificationShade(mFeatureFlags, mResources)); + mShouldUseSplitNotificationShade); mClockPositionAlgorithm.run(mClockPositionResult); boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); boolean animateClock = animate || mAnimateNextPositionUpdate; @@ -1443,7 +1443,7 @@ public class NotificationPanelViewController extends PanelViewController { } mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); - if (animate) { + if (animate && !isFullyCollapsed()) { animateCloseQs(true /* animateAway */); } else { closeQs(); @@ -1727,6 +1727,11 @@ public class NotificationPanelViewController extends PanelViewController { } private float computeQsExpansionFraction() { + if (mQSAnimatingHiddenFromCollapsed) { + // When hiding QS from collapsed state, the expansion can sometimes temporarily + // be larger than 0 because of the timing, leading to flickers. + return 0.0f; + } return Math.min( 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight - mQsMinExpansionHeight)); @@ -2260,19 +2265,24 @@ public class NotificationPanelViewController extends PanelViewController { boolean visible) { // Fancy clipping for quick settings int radius = mScrimCornerRadius; + int statusBarClipTop = 0; + boolean clipStatusView = false; if (!mShouldUseSplitNotificationShade) { // The padding on this area is large enough that we can use a cheaper clipping strategy mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); - mKeyguardStatusViewController.setClipBounds(visible - ? mKeyguardStatusAreaClipBounds : null); + clipStatusView = visible; radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); + statusBarClipTop = top - mKeyguardStatusBar.getTop(); } if (mQs != null) { mQs.setFancyClipping(top, bottom, radius, visible); } + mKeyguardStatusViewController.setClipBounds( + clipStatusView ? mKeyguardStatusAreaClipBounds : null); mScrimController.setNotificationsBounds(left, top, right, bottom); mScrimController.setScrimCornerRadius(radius); + mKeyguardStatusBar.setTopClipping(statusBarClipTop); } private float getQSEdgePosition() { @@ -2527,6 +2537,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onAnimationEnd(Animator animation) { + mQSAnimatingHiddenFromCollapsed = false; mAnimatingQS = false; notifyExpandingFinished(); mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); @@ -2543,6 +2554,7 @@ public class NotificationPanelViewController extends PanelViewController { animator.start(); mQsExpansionAnimator = animator; mQsAnimatorExpand = expanding; + mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index ae018ba4fe76..52f9aca82783 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -152,6 +152,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback)); } + @Override + public void unregisterCallback(StatusBarWindowCallback callback) { + for (int i = 0; i < mCallbacks.size(); i++) { + if (mCallbacks.get(i).get() == callback) { + mCallbacks.remove(i); + return; + } + } + } + /** * Register a listener to monitor scrims visibility * @param listener A listener to monitor scrims visibility 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 ba2340e76831..c27497e43400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -464,6 +464,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final BrightnessSlider.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final List<ExpansionChangedListener> mExpansionChangedListeners; @@ -885,6 +886,8 @@ public class StatusBar extends SystemUI implements DemoMode, mAnimationScheduler = animationScheduler; mStatusBarLocationPublisher = locationPublisher; mFeatureFlags = featureFlags; + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; lockscreenShadeTransitionController.setStatusbar(this); @@ -1369,7 +1372,8 @@ public class StatusBar extends SystemUI implements DemoMode, // are already animating the keyguard dismiss (since we will need to either finish or cancel // the animation). if (trackingTouch - || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) { + || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe() + || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { mKeyguardStateController.notifyKeyguardDismissAmountChanged( 1f - expansion, trackingTouch); } @@ -4386,6 +4390,7 @@ public class StatusBar extends SystemUI implements DemoMode, } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } + updateLightRevealScrimVisibility(); Trace.endSection(); } @@ -4818,6 +4823,11 @@ public class StatusBar extends SystemUI implements DemoMode, return; } + if (mDozeServiceHost.isPulsing()) { + mLightRevealScrim.setVisibility(View.GONE); + return; + } + if (mFeatureFlags.useNewLockscreenAnimations() && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) { mLightRevealScrim.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index 8befe800b4e1..edcf261115d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -23,6 +23,8 @@ import android.util.Pair import android.view.DisplayCutout import android.view.View.LAYOUT_DIRECTION_RTL import android.view.WindowManager +import android.view.WindowMetrics +import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton @@ -118,17 +120,8 @@ class StatusBarContentInsetsProvider @Inject constructor( val chipWidth = rotatedResources.getDimensionPixelSize( R.dimen.ongoing_appops_chip_max_width) - return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) { - Rect(insets.left - dotWidth, - insets.top, - insets.left + chipWidth, - insets.bottom) - } else { - Rect(insets.right - chipWidth, - insets.top, - insets.right + dotWidth, - insets.bottom) - } + val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL + return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl) } /** @@ -139,8 +132,7 @@ class StatusBarContentInsetsProvider @Inject constructor( var insets = insetsByCorner[rotation] if (insets == null) { val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context) - insets = getCalculatedInsetsForRotation(rotation, rotatedResources) - insetsByCorner[rotation] = insets + insets = getAndSetInsetsForRotation(rotation, rotatedResources) } return insets @@ -157,13 +149,19 @@ class StatusBarContentInsetsProvider @Inject constructor( } private fun getCalculatedInsetsForRotation( - @Rotation rotation: Int, + @Rotation targetRotation: Int, rotatedResources: Resources ): Rect { val dc = context.display.cutout + val currentRotation = RotationUtils.getExactRotation(context) return calculateInsetsForRotationWithRotatedResources( - rotation, rotatedResources, dc, windowManager, context) + currentRotation, + targetRotation, + dc, + windowManager.maximumWindowMetrics, + rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height), + rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { @@ -179,8 +177,8 @@ interface StatusBarContentInsetsChangedListener { private const val TAG = "StatusBarInsetsProvider" -private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect { - val bounds = wm.maximumWindowMetrics.bounds +private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect { + val bounds = wm.bounds if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) { return bounds @@ -190,9 +188,24 @@ private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotat return Rect(0, 0, bounds.bottom, bounds.right) } -private fun getCurrentDisplayBounds(wm: WindowManager): Rect { - val bounds = wm.maximumWindowMetrics.bounds - return bounds +@VisibleForTesting +fun getPrivacyChipBoundingRectForInsets( + contentRect: Rect, + dotWidth: Int, + chipWidth: Int, + isRtl: Boolean +): Rect { + return if (isRtl) { + Rect(contentRect.left - dotWidth, + contentRect.top, + contentRect.left + chipWidth, + contentRect.bottom) + } else { + Rect(contentRect.right - chipWidth, + contentRect.top, + contentRect.right + dotWidth, + contentRect.bottom) + } } /** @@ -206,41 +219,32 @@ private fun getCurrentDisplayBounds(wm: WindowManager): Rect { * @see [RotationUtils#getResourcesForRotation] */ fun calculateInsetsForRotationWithRotatedResources( + @Rotation currentRotation: Int, @Rotation targetRotation: Int, - rotatedResources: Resources, displayCutout: DisplayCutout?, - windowmanager: WindowManager, - context: Context + windowMetrics: WindowMetrics, + statusBarHeight: Int, + roundedCornerPadding: Int ): Rect { - val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL - - val exactRotation = RotationUtils.getExactRotation(context) - val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height) - /* TODO: Check if this is ever used for devices with no rounded corners - val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start) - val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end) - val left = if (rtl) paddingEnd else paddingStart - val right = if(rtl) paddingStart else paddingEnd + val left = if (isRtl) paddingEnd else paddingStart + val right = if (isRtl) paddingStart else paddingEnd */ - val roundedCornerPadding = rotatedResources.getDimensionPixelSize( - R.dimen.rounded_corner_content_padding) - - val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation) - val currentBounds = getCurrentDisplayBounds(windowmanager) + val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation) + val currentBounds = windowMetrics.bounds val sbLeftRight = getStatusBarLeftRight( displayCutout, - height, + statusBarHeight, rotZeroBounds.right, rotZeroBounds.bottom, currentBounds.width(), currentBounds.height(), roundedCornerPadding, targetRotation, - exactRotation) + currentRotation) return sbLeftRight } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index b10e841e1bbb..d3953df8c8c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -175,7 +175,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba && !mIsAirplaneMode) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noValidatedNetwork && !mWifiIconState.noNetworksAvailable + } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_available; @@ -380,7 +380,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (noValidatedNetwork && !noNetworksAvailable + } else if (noDefaultNetwork && !noNetworksAvailable && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_available; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java index 9856795c8903..52e0e8a7a0cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java @@ -23,7 +23,6 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.util.AttributeSet; import android.view.View; -import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -35,24 +34,24 @@ import com.android.wm.shell.animation.Interpolators; /** * View to show a toast-like popup on the notification shade and quick settings. */ -public class TapAgainView extends FrameLayout { +public class TapAgainView extends TextView { + private TextView mTextView; + public TapAgainView( @NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); - updateBgColor(); } @Override protected void onFinishInflate() { super.onFinishInflate(); - - TextView text = new TextView(mContext); - text.setText(R.string.notification_tap_again); - addView(text); + updateColor(); } - void updateBgColor() { - setBackgroundResource(R.drawable.rounded_bg_full); + void updateColor() { + int textColor = getResources().getColor(R.color.notif_pill_text, mContext.getTheme()); + setTextColor(textColor); + setBackground(getResources().getDrawable(R.drawable.rounded_bg_full, mContext.getTheme())); } /** Make the view visible. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java index bb53bad7df70..0c5502bac8fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java @@ -44,17 +44,17 @@ public class TapAgainViewController extends ViewController<TapAgainView> { final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onOverlayChanged() { - mView.updateBgColor(); + mView.updateColor(); } @Override public void onUiModeChanged() { - mView.updateBgColor(); + mView.updateColor(); } @Override public void onThemeChanged() { - mView.updateBgColor(); + mView.updateColor(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java index e7201f87b320..af7bf9500bf3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -50,6 +50,13 @@ public interface KeyguardStateController extends CallbackController<Callback> { boolean canDismissLockScreen(); /** + * Whether we can currently perform the shared element SmartSpace transition. This is true if + * we're on the lockscreen, it can be dismissed with a swipe, and the Launcher is underneath the + * keyguard and displaying a SmartSpace that it has registered with System UI. + */ + boolean canPerformSmartSpaceTransition(); + + /** * If the device has PIN/pattern/password or a lock screen at all. */ boolean isMethodSecure(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index e69c1f2573b7..0945a3f884d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -32,6 +32,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -53,6 +54,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private final LockPatternUtils mLockPatternUtils; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new UpdateMonitorCallback(); + private final SmartspaceTransitionController mSmartspaceTransitionController; private boolean mCanDismissLockScreen; private boolean mShowing; @@ -96,10 +98,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum */ @Inject public KeyguardStateControllerImpl(Context context, - KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { + KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, + SmartspaceTransitionController smartspaceTransitionController) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + mSmartspaceTransitionController = smartspaceTransitionController; update(true /* updateAlways */); if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) { @@ -158,6 +162,11 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum mShowing = showing; mOccluded = occluded; notifyKeyguardChanged(); + + // Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard. + // Otherwise, the dismiss amount could be left at a random value if we show/hide during a + // dismiss gesture, canceling the gesture. + notifyKeyguardDismissAmountChanged(showing ? 0f : 1f, false); } private void notifyKeyguardChanged() { @@ -228,6 +237,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override + public boolean canPerformSmartSpaceTransition() { + return canDismissLockScreen() + && mSmartspaceTransitionController.isSmartspaceTransitionPossible(); + } + + @Override public boolean isFaceAuthEnabled() { return mFaceAuthEnabled; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 07e9fed13499..4ab07af92006 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -1023,6 +1023,11 @@ public class NetworkControllerImpl extends BroadcastReceiver mValidatedTransports.clear(); if (mLastDefaultNetworkCapabilities != null) { for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) { + if (transportType != NetworkCapabilities.TRANSPORT_CELLULAR + && transportType != NetworkCapabilities.TRANSPORT_WIFI + && transportType != NetworkCapabilities.TRANSPORT_ETHERNET) { + continue; + } if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) { mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); @@ -1045,11 +1050,15 @@ public class NetworkControllerImpl extends BroadcastReceiver Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); } - mInetCondition = !mValidatedTransports.isEmpty(); + mInetCondition = mValidatedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); pushConnectivityToSignals(); if (mProviderModel) { - mNoDefaultNetwork = mConnectedTransports.isEmpty(); + mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable); for (int i = 0; i < mMobileSignalControllers.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index 4284148c38a8..c5e35a497956 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -22,16 +22,19 @@ import android.annotation.Nullable; import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import android.widget.ToastPresenter; import com.android.internal.R; import com.android.launcher3.icons.IconFactory; @@ -52,7 +55,6 @@ public class SystemUIToast implements ToastPlugin.Toast { private final String mPackageName; private final int mUserId; private final LayoutInflater mLayoutInflater; - private final boolean mToastStyleEnabled; final int mDefaultX = 0; final int mDefaultHorizontalMargin = 0; @@ -66,15 +68,14 @@ public class SystemUIToast implements ToastPlugin.Toast { @Nullable private final Animator mOutAnimator; SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, - String packageName, int userId, boolean toastStyleEnabled, int orientation) { + String packageName, int userId, int orientation) { this(layoutInflater, context, text, null, packageName, userId, - toastStyleEnabled, orientation); + orientation); } SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, ToastPlugin.Toast pluginToast, String packageName, int userId, - boolean toastStyleEnabled, int orientation) { - mToastStyleEnabled = toastStyleEnabled; + int orientation) { mLayoutInflater = layoutInflater; mContext = context; mText = text; @@ -167,23 +168,45 @@ public class SystemUIToast implements ToastPlugin.Toast { return mPluginToast.getView(); } - View toastView; - if (mToastStyleEnabled) { - toastView = mLayoutInflater.inflate( + final View toastView = mLayoutInflater.inflate( com.android.systemui.R.layout.text_toast, null); - ((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText); + final TextView textView = toastView.findViewById(com.android.systemui.R.id.text); + final ImageView iconView = toastView.findViewById(com.android.systemui.R.id.icon); + textView.setText(mText); + + ApplicationInfo appInfo = null; + try { + appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(mPackageName, 0, mUserId); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Package name not found package=" + mPackageName + + " user=" + mUserId); + } + + if (appInfo != null && appInfo.targetSdkVersion < Build.VERSION_CODES.S) { + // no two-line limit + textView.setMaxLines(Integer.MAX_VALUE); + // no app icon + toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE); + } else { Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId); if (icon == null) { - toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE); + iconView.setVisibility(View.GONE); } else { - ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon)) - .setImageDrawable(icon); + iconView.setImageDrawable(icon); + if (appInfo.labelRes != 0) { + try { + Resources res = mContext.getPackageManager().getResourcesForApplication( + appInfo, + new Configuration(mContext.getResources().getConfiguration())); + iconView.setContentDescription(res.getString(appInfo.labelRes)); + } catch (PackageManager.NameNotFoundException e) { + Log.d(TAG, "Cannot find application resources for icon label."); + } + } } - } else { - toastView = ToastPresenter.getTextToastView(mContext, mText); } - return toastView; } @@ -205,18 +228,14 @@ public class SystemUIToast implements ToastPlugin.Toast { return mPluginToast.getInAnimation(); } - return mToastStyleEnabled - ? ToastDefaultAnimation.Companion.toastIn(getView()) - : null; + return ToastDefaultAnimation.Companion.toastIn(getView()); } private Animator createOutAnimator() { if (isPluginToast() && mPluginToast.getOutAnimation() != null) { return mPluginToast.getOutAnimation(); } - return mToastStyleEnabled - ? ToastDefaultAnimation.Companion.toastOut(getView()) - : null; + return ToastDefaultAnimation.Companion.toastOut(getView()); } /** @@ -225,6 +244,10 @@ public class SystemUIToast implements ToastPlugin.Toast { */ public static Drawable getBadgedIcon(@NonNull Context context, String packageName, int userId) { + if (!(context.getApplicationContext() instanceof Application)) { + return null; + } + final ApplicationsState appState = ApplicationsState.getInstance((Application) context.getApplicationContext()); if (!appState.isUserAdded(userId)) { diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java index 8b782d4b7923..148bffa0000e 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java @@ -27,7 +27,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.ToastPlugin; import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.statusbar.FeatureFlags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -43,17 +42,14 @@ public class ToastFactory implements Dumpable { // only one ToastPlugin can be connected at a time. private ToastPlugin mPlugin; private final LayoutInflater mLayoutInflater; - private final boolean mToastStyleEnabled; @Inject public ToastFactory( LayoutInflater layoutInflater, PluginManager pluginManager, - DumpManager dumpManager, - FeatureFlags featureFlags) { + DumpManager dumpManager) { mLayoutInflater = layoutInflater; dumpManager.registerDumpable("ToastFactory", this); - mToastStyleEnabled = featureFlags.isToastStyleEnabled(); pluginManager.addPluginListener( new PluginListener<ToastPlugin>() { @Override @@ -77,10 +73,10 @@ public class ToastFactory implements Dumpable { int userId, int orientation) { if (isPluginAvailable()) { return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text, - packageName, userId), packageName, userId, mToastStyleEnabled, orientation); + packageName, userId), packageName, userId, orientation); } return new SystemUIToast(mLayoutInflater, context, text, packageName, userId, - mToastStyleEnabled, orientation); + orientation); } private boolean isPluginAvailable() { @@ -91,6 +87,5 @@ public class ToastFactory implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("ToastFactory:"); pw.println(" mAttachedPlugin=" + mPlugin); - pw.println(" mToastStyleEnabled=" + mToastStyleEnabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 92ea1d0e5fbd..42f66875e7a1 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -19,6 +19,7 @@ package com.android.systemui.toast; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.ToastPresenter; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.VisibleForTesting; + import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; @@ -60,11 +62,11 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private final AccessibilityManager mAccessibilityManager; private final ToastFactory mToastFactory; private final ToastLogger mToastLogger; - private SystemUIToast mToast; @Nullable private ToastPresenter mPresenter; @Nullable private ITransientNotificationCallback mCallback; private ToastOutAnimatorListener mToastOutAnimatorListener; + @VisibleForTesting SystemUIToast mToast; private int mOrientation = ORIENTATION_PORTRAIT; @Inject @@ -191,7 +193,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { /** * Once the out animation for a toast is finished, start showing the next toast. */ - class ToastOutAnimatorListener implements Animator.AnimatorListener { + class ToastOutAnimatorListener extends AnimatorListenerAdapter { final ToastPresenter mPrevPresenter; final ITransientNotificationCallback mPrevCallback; @Nullable Runnable mShowNextToastRunnable; @@ -210,10 +212,6 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { } @Override - public void onAnimationStart(Animator animation) { - } - - @Override public void onAnimationEnd(Animator animation) { mPrevPresenter.hide(mPrevCallback); if (mShowNextToastRunnable != null) { @@ -221,15 +219,5 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { } mToastOutAnimatorListener = null; } - - @Override - public void onAnimationCancel(Animator animation) { - onAnimationEnd(animation); - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 7244ffed61b9..26f4a2b9d2b2 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -68,8 +68,7 @@ public class TunerServiceImpl extends TunerService { private static final String[] RESET_EXCEPTION_LIST = new String[] { QSTileHost.TILES_SETTING, Settings.Secure.DOZE_ALWAYS_ON, - Settings.Secure.MEDIA_CONTROLS_RESUME, - Secure.MEDIA_CONTROLS_RESUME_BLOCKED + Settings.Secure.MEDIA_CONTROLS_RESUME }; private final Observer mObserver = new Observer(); diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index f3a95f711b7c..bf006672f4d9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -23,7 +23,6 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.provider.Settings; -import android.text.TextUtils; import android.view.ContextThemeWrapper; import android.view.View; @@ -32,9 +31,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.function.Consumer; public class Utils { @@ -144,7 +141,7 @@ public class Utils { /** * Allow media resumption controls. Requires {@link #useQsMediaPlayer(Context)} to be enabled. - * Off by default, but can be enabled by setting to 1 + * On by default, but can be disabled by setting to 0 */ public static boolean useMediaResumption(Context context) { int flag = Settings.Secure.getInt(context.getContentResolver(), @@ -153,20 +150,14 @@ public class Utils { } /** - * Get the set of apps for which the user has manually disabled resumption. + * Allow recommendations from smartspace to show in media controls. + * Requires {@link #useQsMediaPlayer(Context)} to be enabled. + * On by default, but can be disabled by setting to 0 */ - public static Set<String> getBlockedMediaApps(Context context) { - String list = Settings.Secure.getString(context.getContentResolver(), - Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED); - if (TextUtils.isEmpty(list)) { - return new HashSet<>(); - } - String[] names = list.split(":"); - Set<String> apps = new HashSet<>(names.length); - for (String s : names) { - apps.add(s); - } - return apps; + public static boolean allowMediaRecommendations(Context context) { + int flag = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1); + return useQsMediaPlayer(context) && flag > 0; } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java index b9b7730c67f3..0b2f004537d6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java @@ -116,7 +116,7 @@ public class RotationUtils { default: throw new IllegalArgumentException("Unknown rotation: " + rot); } - Configuration c = context.getResources().getConfiguration(); + Configuration c = new Configuration(context.getResources().getConfiguration()); c.orientation = orientation; Context rotated = context.createConfigurationContext(c); return rotated.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 50b885815e09..961822a598e7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -337,37 +337,44 @@ public class VolumeDialogImpl implements VolumeDialog, mTouchableRegion.setEmpty(); - // Set the touchable region to the union of all child view bounds. We don't use touches on - // the volume dialog container itself, so this is fine. + // Set the touchable region to the union of all child view bounds and the live caption + // tooltip. We don't use touches on the volume dialog container itself, so this is fine. for (int i = 0; i < mDialogView.getChildCount(); i++) { - final View view = mDialogView.getChildAt(i); - final int[] locInWindow = new int[2]; - view.getLocationInWindow(locInWindow); - - float x = locInWindow[0]; - float y = locInWindow[1]; - - // The ringer and rows container has extra height at the top to fit the expanded ringer - // drawer. This area should not be touchable unless the ringer drawer is open. - if (view == mTopContainer && !mIsRingerDrawerOpen) { - if (!isLandscape()) { - y += getRingerDrawerOpenExtraSize(); - } else { - x += getRingerDrawerOpenExtraSize(); - } - } + unionViewBoundstoTouchableRegion(mDialogView.getChildAt(i)); + } - mTouchableRegion.op( - (int) x, - (int) y, - locInWindow[0] + view.getWidth(), - locInWindow[1] + view.getHeight(), - Region.Op.UNION); + if (mODICaptionsTooltipView != null && mODICaptionsTooltipView.getVisibility() == VISIBLE) { + unionViewBoundstoTouchableRegion(mODICaptionsTooltipView); } internalInsetsInfo.touchableRegion.set(mTouchableRegion); } + private void unionViewBoundstoTouchableRegion(final View view) { + final int[] locInWindow = new int[2]; + view.getLocationInWindow(locInWindow); + + float x = locInWindow[0]; + float y = locInWindow[1]; + + // The ringer and rows container has extra height at the top to fit the expanded ringer + // drawer. This area should not be touchable unless the ringer drawer is open. + if (view == mTopContainer && !mIsRingerDrawerOpen) { + if (!isLandscape()) { + y += getRingerDrawerOpenExtraSize(); + } else { + x += getRingerDrawerOpenExtraSize(); + } + } + + mTouchableRegion.op( + (int) x, + (int) y, + locInWindow[0] + view.getWidth(), + locInWindow[1] + view.getHeight(), + Region.Op.UNION); + } + private void initDialog() { mDialog = new CustomDialog(mContext); @@ -1058,21 +1065,38 @@ public class VolumeDialogImpl implements VolumeDialog, } if (mODICaptionsTooltipView != null) { - mODICaptionsTooltipView.setAlpha(0.f); - mODICaptionsTooltipView.animate() - .alpha(1.f) - .setStartDelay(mDialogShowAnimationDurationMs) - .withEndAction(() -> { - if (D.BUG) Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true"); - Prefs.putBoolean(mContext, - Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true); - mHasSeenODICaptionsTooltip = true; - if (mODICaptionsIcon != null) { - mODICaptionsIcon - .postOnAnimation(getSinglePressFor(mODICaptionsIcon)); - } - }) - .start(); + mODICaptionsTooltipView.setAlpha(0.0f); + + // We need to wait for layout and then center the caption view. Since the height of the + // dialog is now dynamic (with the variable ringer drawer height changing the height of + // the dialog), we need to do this here in code vs. in XML. + mHandler.post(() -> { + final int[] odiTooltipLocation = mODICaptionsTooltipView.getLocationOnScreen(); + final int[] odiButtonLocation = mODICaptionsIcon.getLocationOnScreen(); + + final float heightDiffForCentering = + (mODICaptionsTooltipView.getHeight() - mODICaptionsIcon.getHeight()) / 2f; + + mODICaptionsTooltipView.setTranslationY( + odiButtonLocation[1] - odiTooltipLocation[1] - heightDiffForCentering); + + mODICaptionsTooltipView.animate() + .alpha(1.0f) + .setStartDelay(mDialogShowAnimationDurationMs) + .withEndAction(() -> { + if (D.BUG) { + Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true"); + } + Prefs.putBoolean(mContext, + Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true); + mHasSeenODICaptionsTooltip = true; + if (mODICaptionsIcon != null) { + mODICaptionsIcon + .postOnAnimation(getSinglePressFor(mODICaptionsIcon)); + } + }) + .start(); + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java new file mode 100644 index 000000000000..9d0cc6a00ec0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -0,0 +1,207 @@ +/* + * 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 com.android.systemui.wallet.controller; + +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; + +import android.content.Context; +import android.database.ContentObserver; +import android.provider.Settings; +import android.service.quickaccesswallet.GetWalletCardsRequest; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.service.quickaccesswallet.QuickAccessWalletClientImpl; +import android.util.Log; + +import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.settings.SecureSettings; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * Controller to handle communication between SystemUI and Quick Access Wallet Client. + */ +@SysUISingleton +public class QuickAccessWalletController { + + /** + * Event for the wallet status change, e.g. the default payment app change and the wallet + * preference change. + */ + public enum WalletChangeEvent { + DEFAULT_PAYMENT_APP_CHANGE, + WALLET_PREFERENCE_CHANGE, + } + + private static final String TAG = "QAWController"; + private final Context mContext; + private final Executor mExecutor; + private final SecureSettings mSecureSettings; + + private QuickAccessWalletClient mQuickAccessWalletClient; + private ContentObserver mWalletPreferenceObserver; + private ContentObserver mDefaultPaymentAppObserver; + private int mWalletPreferenceChangeEvents = 0; + private int mDefaultPaymentAppChangeEvents = 0; + private boolean mWalletEnabled = false; + + @Inject + public QuickAccessWalletController( + Context context, + @Main Executor executor, + SecureSettings secureSettings, + QuickAccessWalletClient quickAccessWalletClient) { + mContext = context; + mExecutor = executor; + mSecureSettings = secureSettings; + mQuickAccessWalletClient = quickAccessWalletClient; + } + + /** + * Returns true if the Quick Access Wallet service & feature is available. + */ + public boolean isWalletEnabled() { + return mWalletEnabled; + } + + /** + * Returns the current instance of {@link QuickAccessWalletClient} in the controller. + */ + public QuickAccessWalletClient getWalletClient() { + return mQuickAccessWalletClient; + } + + /** + * Setup the wallet change observers per {@link WalletChangeEvent} + * + * @param cardsRetriever a callback that retrieves the wallet cards + * @param events {@link WalletChangeEvent} need to be handled. + */ + public void setupWalletChangeObservers( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, + WalletChangeEvent... events) { + for (WalletChangeEvent event : events) { + if (event == WALLET_PREFERENCE_CHANGE) { + setupWalletPreferenceObserver(); + } else if (event == DEFAULT_PAYMENT_APP_CHANGE) { + setupDefaultPaymentAppObserver(cardsRetriever); + } + } + } + + /** + * Unregister wallet change observers per {@link WalletChangeEvent} if needed. + * + */ + public void unregisterWalletChangeObservers(WalletChangeEvent... events) { + for (WalletChangeEvent event : events) { + if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) { + mWalletPreferenceChangeEvents--; + if (mWalletPreferenceChangeEvents == 0) { + mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver); + } + } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) { + mDefaultPaymentAppChangeEvents--; + if (mDefaultPaymentAppChangeEvents == 0) { + mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver); + } + } + } + } + + /** + * Update the "show wallet" preference. + */ + public void updateWalletPreference() { + mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable() + && mQuickAccessWalletClient.isWalletFeatureAvailable() + && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked(); + } + + /** + * Query the wallet cards from {@link QuickAccessWalletClient}. + * + * @param cardsRetriever a callback to retrieve wallet cards. + */ + public void queryWalletCards( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (!mWalletEnabled) { + Log.w(TAG, "QuickAccessWallet is unavailable, unable to query cards."); + return; + } + int cardWidth = + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); + int cardHeight = + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); + int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); + GetWalletCardsRequest request = + new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); + mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever); + } + + /** + * Re-create the {@link QuickAccessWalletClient} of the controller. + */ + public void reCreateWalletClient() { + mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext); + } + + private void setupDefaultPaymentAppObserver( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (mDefaultPaymentAppObserver == null) { + mDefaultPaymentAppObserver = new ContentObserver(null /* handler */) { + @Override + public void onChange(boolean selfChange) { + mExecutor.execute(() -> { + reCreateWalletClient(); + updateWalletPreference(); + queryWalletCards(cardsRetriever); + }); + } + }; + + mSecureSettings.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), + false /* notifyForDescendants */, + mDefaultPaymentAppObserver); + } + mDefaultPaymentAppChangeEvents++; + } + + private void setupWalletPreferenceObserver() { + if (mWalletPreferenceObserver == null) { + mWalletPreferenceObserver = new ContentObserver(null /* handler */) { + @Override + public void onChange(boolean selfChange) { + mExecutor.execute(() -> { + updateWalletPreference(); + }); + } + }; + + mSecureSettings.registerContentObserver( + Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY), + false /* notifyForDescendants */, + mWalletPreferenceObserver); + } + mWalletPreferenceChangeEvents++; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 83aa01f8d393..c6123e77076d 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.Window; @@ -52,7 +53,7 @@ import javax.inject.Inject; */ public class WalletActivity extends LifecycleActivity { - private final QuickAccessWalletClient mQuickAccessWalletClient; + private static final String TAG = "WalletActivity"; private final KeyguardStateController mKeyguardStateController; private final KeyguardDismissUtil mKeyguardDismissUtil; private final ActivityStarter mActivityStarter; @@ -65,7 +66,6 @@ public class WalletActivity extends LifecycleActivity { @Inject public WalletActivity( - QuickAccessWalletClient quickAccessWalletClient, KeyguardStateController keyguardStateController, KeyguardDismissUtil keyguardDismissUtil, ActivityStarter activityStarter, @@ -74,7 +74,6 @@ public class WalletActivity extends LifecycleActivity { FalsingManager falsingManager, UserTracker userTracker, StatusBarKeyguardViewManager keyguardViewManager) { - mQuickAccessWalletClient = quickAccessWalletClient; mKeyguardStateController = keyguardStateController; mKeyguardDismissUtil = keyguardDismissUtil; mActivityStarter = activityStarter; @@ -103,10 +102,11 @@ public class WalletActivity extends LifecycleActivity { getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close); WalletView walletView = requireViewById(R.id.wallet_view); + QuickAccessWalletClient walletClient = QuickAccessWalletClient.create(this); mWalletScreenController = new WalletScreenController( this, walletView, - mQuickAccessWalletClient, + walletClient, mActivityStarter, mExecutor, mHandler, @@ -116,6 +116,10 @@ public class WalletActivity extends LifecycleActivity { walletView.getAppButton().setOnClickListener( v -> { + if (walletClient.createWalletIntent() == null) { + Log.w(TAG, "Unable to create wallet app intent."); + return; + } if (!mKeyguardStateController.isUnlocked() && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; @@ -123,12 +127,12 @@ public class WalletActivity extends LifecycleActivity { if (mKeyguardStateController.isUnlocked()) { mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true); + walletClient.createWalletIntent(), true); finish(); } else { mKeyguardDismissUtil.executeWhenUnlocked(() -> { mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true); + walletClient.createWalletIntent(), true); finish(); return false; }, false, true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 10322f287d50..06b0bb25e01c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -40,8 +40,10 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -86,6 +88,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock Resources mResources; + KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock + SmartspaceTransitionController mSmartSpaceTransitionController; @Mock private ClockPlugin mClockPlugin; @Mock @@ -135,7 +140,10 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mBatteryController, mKeyguardUpdateMonitor, mBypassController, - mSmartspaceController); + mSmartspaceController, + mKeyguardUnlockAnimationController, + mSmartSpaceTransitionController + ); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 49f1655997ef..f9b6d4467e3c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -22,6 +22,8 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -50,6 +52,10 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { ConfigurationController mConfigurationController; @Mock DozeParameters mDozeParameters; + @Mock + KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock + SmartspaceTransitionController mSmartSpaceTransitionController; private KeyguardStatusViewController mController; @@ -64,7 +70,9 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { mKeyguardStateController, mKeyguardUpdateMonitor, mConfigurationController, - mDozeParameters); + mDozeParameters, + mKeyguardUnlockAnimationController, + mSmartSpaceTransitionController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index d6b82f355e7c..2d19f7dd4706 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -55,7 +55,6 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock -import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -329,11 +328,6 @@ public class MediaControlPanelTest : SysuiTestCase() { assertThat(dismiss.isEnabled).isEqualTo(true) dismiss.callOnClick() - val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) - verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean(), - eq(false)) - - captor.value.onDismiss() verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 59527f6b9740..dfb149daa6d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -8,6 +8,7 @@ import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession +import android.provider.Settings import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper @@ -83,11 +84,16 @@ class MediaDataManagerTest : SysuiTestCase() { @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> private val clock = FakeSystemClock() + private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1) + @Before fun setup() { foregroundExecutor = FakeExecutor(clock) backgroundExecutor = FakeExecutor(clock) smartspaceMediaDataProvider = SmartspaceMediaDataProvider() + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1) mediaDataManager = MediaDataManager( context = context, backgroundExecutor = backgroundExecutor, @@ -139,6 +145,8 @@ class MediaDataManagerTest : SysuiTestCase() { fun tearDown() { session.release() mediaDataManager.destroy() + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, originalSmartspaceSetting) } @Test @@ -257,55 +265,6 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test - fun testAppBlockedFromResumption() { - // GIVEN that the manager has a notification with a resume action - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) - val data = mediaDataCaptor.value - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // and the manager should block the package from creating resume controls - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // WHEN the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the media data is removed - verify(listener).onMediaDataRemoved(eq(KEY)) - } - - @Test - fun testAppUnblockedFromResumption() { - // GIVEN that an app was blocked from resuming - val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app") - mediaDataManager.appsBlockedFromResume = blocked - - // and GIVEN that the manager has a notification from that app with a resume action - whenever(controller.metadata).thenReturn(metadataBuilder.build()) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) - val data = mediaDataCaptor.value - assertThat(data.resumption).isFalse() - mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) - - // WHEN the app is unblocked - mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app") - - // and the notification is removed - mediaDataManager.onNotificationRemoved(KEY) - - // THEN the entry will stay as a resume control - verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor)) - } - - @Test fun testAddResumptionControls() { // WHEN resumption controls are added val desc = MediaDescription.Builder().run { @@ -382,6 +341,18 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testOnSmartspaceMediaDataLoaded_settingDisabled_doesNothing() { + // WHEN media recommendation setting is off + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0) + smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) + + // THEN smartspace signal is ignored + verify(listener, never()) + .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean()) + } + + @Test fun testOnMediaDataChanged_updatesLastActiveTime() { val currentTime = clock.elapsedRealtime() mediaDataManager.onNotificationAdded(KEY, mediaNotification) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 96d1d94795f6..4e1627ff0343 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -89,6 +89,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> + @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable> private lateinit var executor: FakeExecutor private lateinit var data: MediaData @@ -224,9 +225,6 @@ class MediaResumeListenerTest : SysuiTestCase() { // But we do not tell it to add new controls verify(mediaDataManager, never()) .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any()) - - // Finally, make sure the resume browser disconnected - verify(resumeBrowser).disconnect() } @Test @@ -267,4 +265,39 @@ class MediaResumeListenerTest : SysuiTestCase() { verify(mediaDataManager, times(3)).addResumptionControls(anyInt(), any(), any(), any(), any(), any(), eq(PACKAGE_NAME)) } + + @Test + fun testGetResumeAction_restarts() { + // Set up mocks to successfully find a MBS that returns valid media + val pm = mock(PackageManager::class.java) + whenever(mockContext.packageManager).thenReturn(pm) + val resolveInfo = ResolveInfo() + val serviceInfo = ServiceInfo() + serviceInfo.packageName = PACKAGE_NAME + resolveInfo.serviceInfo = serviceInfo + resolveInfo.serviceInfo.name = CLASS_NAME + val resumeInfo = listOf(resolveInfo) + whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo) + + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.testConnection()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // When media data is loaded that has not been checked yet, and does have a MBS + val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false) + resumeListener.onMediaDataLoaded(KEY, null, dataCopy) + + // Then we test whether the service is valid and set the resume action + executor.runAllReady() + verify(resumeBrowser).testConnection() + verify(mediaDataManager).setResumeAction(eq(KEY), capture(actionCaptor)) + + // When the resume action is run + actionCaptor.value.run() + + // Then we call restart + verify(resumeBrowser).restart() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt index d26229edf71a..dfa7c66b38f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt @@ -91,8 +91,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.testConnection() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -111,8 +112,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNoResults() resumeBrowser.testConnection() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -132,8 +134,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -143,8 +146,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { whenever(browser.getRoot()).thenReturn(null) resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -163,8 +167,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNoResults() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -173,8 +178,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNotPlayable() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -193,8 +199,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.restart() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -202,13 +209,11 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { // When restart is called and we connect successfully setupBrowserConnection() resumeBrowser.restart() + verify(callback).onConnected() // Then it creates a new controller and sends play command verify(transportControls).prepare() verify(transportControls).play() - - // Then it calls onConnected - verify(callback).onConnected() } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 7533cf1310de..b09afab3d242 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -73,6 +73,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.google.common.util.concurrent.MoreExecutors; @@ -119,6 +120,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock + private QuickAccessWalletController mController; + @Mock private FeatureFlags mFeatureFlags; @Captor ArgumentCaptor<Intent> mIntentCaptor; @@ -145,6 +148,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL); when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true); + when(mController.getWalletClient()).thenReturn(mQuickAccessWalletClient); mTile = new QuickAccessWalletTile( mHost, @@ -155,11 +160,11 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mStatusBarStateController, mActivityStarter, mQSLogger, - mQuickAccessWalletClient, mKeyguardStateController, mPackageManager, mSecureSettings, MoreExecutors.directExecutor(), + mController, mFeatureFlags); } @@ -175,6 +180,15 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testWalletServiceUnavailable_recreateWalletClient() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mTile.handleSetListening(true); + + verify(mController, times(1)).reCreateWalletClient(); + } + + @Test public void testIsAvailable_qawFeatureAvailable() { when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true); when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); @@ -330,17 +344,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { public void testHandleSetListening_queryCards() { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient) - .getWalletCards(any(), mRequestCaptor.capture(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); - GetWalletCardsRequest request = mRequestCaptor.getValue(); - assertEquals( - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width), - request.getCardWidthPx()); - assertEquals( - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), - request.getCardHeightPx()); - assertEquals(1, request.getMaxCards()); assertThat(mCallbackCaptor.getValue()).isInstanceOf( QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class); } @@ -354,37 +359,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test - public void testState_queryCards_hasCards_then_noCards() { - when(mKeyguardStateController.isUnlocked()).thenReturn(true); - GetWalletCardsResponse responseWithCards = - new GetWalletCardsResponse( - Collections.singletonList(createWalletCard(mContext)), 0); - GetWalletCardsResponse responseWithoutCards = - new GetWalletCardsResponse(Collections.EMPTY_LIST, 0); - - mTile.handleSetListening(true); - - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); - - // query wallet cards, has cards - mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards); - mTestableLooper.processAllMessages(); - - assertNotNull(mTile.getState().sideViewCustomDrawable); - - mTile.handleSetListening(true); - - verify(mQuickAccessWalletClient, times(2)) - .getWalletCards(any(), any(), mCallbackCaptor.capture()); - - // query wallet cards, has no cards - mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards); - mTestableLooper.processAllMessages(); - - assertNull(mTile.getState().sideViewCustomDrawable); - } - - @Test public void testQueryCards_noCards_notUpdateSideViewDrawable() { setUpWalletCard(/* hasCard= */ false); @@ -398,7 +372,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); mCallbackCaptor.getValue().onWalletCardRetrievalError(error); mTestableLooper.processAllMessages(); @@ -422,7 +396,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); mCallbackCaptor.getValue().onWalletCardsRetrieved(response); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 9b5c33d64eb9..116f807a888d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -511,6 +511,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setNextAlarm(image: Drawable?, description: String?) { } + + override fun setMediaTarget(target: SmartspaceTarget?) { + } }) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 83a1872a0a45..ee8d1209a5cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -42,7 +42,6 @@ import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; import android.os.PowerManager; import android.os.UserManager; -import android.service.quickaccesswallet.QuickAccessWalletClient; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; @@ -112,6 +111,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; import org.junit.Before; @@ -252,8 +252,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private LockIconViewController mLockIconViewController; @Mock - private QuickAccessWalletClient mQuickAccessWalletClient; - @Mock private KeyguardMediaController mKeyguardMediaController; @Mock private PrivacyDotViewController mPrivacyDotViewController; @@ -267,6 +265,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private FragmentService mFragmentService; @Mock private FragmentHostManager mFragmentHostManager; + @Mock + private QuickAccessWalletController mQuickAccessWalletController; private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; @@ -380,11 +380,11 @@ public class NotificationPanelViewTest extends SysuiTestCase { mAmbientState, mLockIconViewController, mFeatureFlags, - mQuickAccessWalletClient, mKeyguardMediaController, mPrivacyDotViewController, mTapAgainViewController, mFragmentService, + mQuickAccessWalletController, new FakeExecutor(new FakeSystemClock()), mSecureSettings); mNotificationPanelViewController.initDependencies( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt new file mode 100644 index 000000000000..4796cd768980 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -0,0 +1,368 @@ +/* + * 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 com.android.systemui.statusbar.phone + +import android.graphics.Rect +import android.test.suitebuilder.annotation.SmallTest +import android.view.DisplayCutout +import android.view.WindowMetrics +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.leak.RotationUtils +import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE +import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN +import com.android.systemui.util.leak.RotationUtils.Rotation +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +class StatusBarContentInsetsProviderTest : SysuiTestCase() { + @Mock private lateinit var dc: DisplayCutout + @Mock private lateinit var windowMetrics: WindowMetrics + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testGetBoundingRectForPrivacyChipForRotation_noCutout() { + val screenBounds = Rect(0, 0, 1080, 2160) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + val chipWidth = 30 + val dotWidth = 10 + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + + var isRtl = false + var targetRotation = ROTATION_NONE + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 1080 - 20 (rounded corner) - 30 (chip), + * 0 (sb top) + * 1080 - 20 (rounded corner) + 10 ( dot), + * 100 (sb height portrait) + */ + var expected = Rect(1030, 0, 1070, 100) + assertRects(expected, chipBounds, currentRotation, targetRotation) + isRtl = true + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 0 + 20 (rounded corner) - 10 (dot), + * 0 (sb top) + * 0 + 20 (rounded corner) + 30 (chip), + * 100 (sb height portrait) + */ + expected = Rect(10, 0, 50, 100) + assertRects(expected, chipBounds, currentRotation, targetRotation) + + isRtl = false + targetRotation = ROTATION_LANDSCAPE + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 2160 - 20 (rounded corner) - 30 (chip), + * 0 (sb top) + * 2160 - 20 (rounded corner) + 10 ( dot), + * 60 (sb height landscape) + */ + expected = Rect(2110, 0, 2150, 60) + assertRects(expected, chipBounds, currentRotation, targetRotation) + isRtl = true + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 0 + 20 (rounded corner) - 10 (dot), + * 0 (sb top) + * 0 + 20 (rounded corner) + 30 (chip), + * 60 (sb height landscape) + */ + expected = Rect(10, 0, 50, 60) + assertRects(expected, chipBounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() { + // GIVEN a device in portrait mode with width < height and a display cutout in the top-left + val screenBounds = Rect(0, 0, 1080, 2160) + val dcBounds = Rect(0, 0, 100, 100) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + `when`(dc.boundingRects).thenReturn(listOf(dcBounds)) + + // THEN rotations which share a short side should use the greater value between rounded + // corner padding and the display cutout's size + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(dcBounds.right, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(dcBounds.height(), + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + // THEN the side that does NOT share a short side with the display cutout ignores the + // display cutout bounds + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.width() - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + // Phone in portrait, seascape (rot_270) bounds + targetRotation = ROTATION_SEASCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - dcBounds.height(), + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() { + // GIVEN phone in portrait mode, where width < height and the cutout is not in the corner + // the assumption here is that if the cutout does NOT touch the corner then we have room to + // layout the status bar in the given space. + + val screenBounds = Rect(0, 0, 1080, 2160) + // cutout centered at the top + val dcBounds = Rect(490, 0, 590, 100) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + `when`(dc.boundingRects).thenReturn(listOf(dcBounds)) + + // THEN only the landscape/seascape rotations should avoid the cutout area because of the + // potential letterboxing + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(dcBounds.height(), + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_SEASCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - dcBounds.height(), + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_noCutout() { + // GIVEN device in portrait mode, where width < height and no cutout + val currentRotation = ROTATION_NONE + val screenBounds = Rect(0, 0, 1080, 2160) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + + // THEN content insets should only use rounded corner padding + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.width() - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + private fun assertRects( + expected: Rect, + actual: Rect, + @Rotation currentRotation: Int, + @Rotation targetRotation: Int + ) { + assertTrue( + "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + + " targetRotation=${RotationUtils.toString(targetRotation)}" + + " expected=$expected actual=$actual", + expected.equals(actual)) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index 53a2efce9bb8..4b87ec895773 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import org.junit.Before; import org.junit.Test; @@ -49,12 +50,14 @@ public class KeyguardStateControllerTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; private KeyguardStateController mKeyguardStateController; + @Mock + private SmartspaceTransitionController mSmartSpaceTransitionController; @Before public void setup() { MockitoAnnotations.initMocks(this); mKeyguardStateController = new KeyguardStateControllerImpl(mContext, - mKeyguardUpdateMonitor, mLockPatternUtils); + mKeyguardUpdateMonitor, mLockPatternUtils, mSmartSpaceTransitionController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 365c62cddbdf..9b177e1cd0e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -17,7 +17,6 @@ package com.android.systemui.toast; import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; -import static android.widget.ToastPresenter.TEXT_TOAST_LAYOUT; import static com.google.common.truth.Truth.assertThat; @@ -31,13 +30,20 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Application; import android.app.INotificationManager; import android.app.ITransientNotificationCallback; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Binder; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -47,12 +53,11 @@ import android.view.accessibility.IAccessibilityManager; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; -import android.widget.ToastPresenter; import androidx.test.filters.SmallTest; -import com.android.internal.R; import com.android.internal.util.IntPair; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.plugins.PluginManager; @@ -70,6 +75,7 @@ import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class ToastUITest extends SysuiTestCase { private static final int ANDROID_UID = 1000; private static final int SYSTEMUI_UID = 10140; @@ -85,12 +91,14 @@ public class ToastUITest extends SysuiTestCase { private static final Binder WINDOW_TOKEN_2 = new Binder(); private static final String TEXT = "Hello World"; - private static final int MESSAGE_RES_ID = R.id.message; + private static final int MESSAGE_RES_ID = R.id.text; private Context mContextSpy; private ToastUI mToastUI; - @Mock private LayoutInflater mLayoutInflater; + private View mToastView; + @Mock private Application mApplication; @Mock private CommandQueue mCommandQueue; + @Mock private LayoutInflater mLayoutInflater; @Mock private WindowManager mWindowManager; @Mock private INotificationManager mNotificationManager; @Mock private IAccessibilityManager mAccessibilityManager; @@ -98,6 +106,7 @@ public class ToastUITest extends SysuiTestCase { @Mock private DumpManager mDumpManager; @Mock private ToastLogger mToastLogger; @Mock private FeatureFlags mFeatureFlags; + @Mock private PackageManager mPackageManager; @Mock private ITransientNotificationCallback mCallback; @Captor private ArgumentCaptor<View> mViewCaptor; @@ -106,29 +115,33 @@ public class ToastUITest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn( - ToastPresenter.getTextToastView(mContext, TEXT)); - when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false); - + mToastView = LayoutInflater.from(mContext).inflate(R.layout.text_toast, null); + when(mLayoutInflater.inflate(anyInt(), eq(null))).thenReturn(mToastView); mContext.addMockSystemService(WindowManager.class, mWindowManager); mContextSpy = spy(mContext); + when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); - - doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); - mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager, - mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager, - mDumpManager, mFeatureFlags), mToastLogger); + mToastUI = new ToastUI( + mContextSpy, + mCommandQueue, + mNotificationManager, + mAccessibilityManager, + new ToastFactory( + mLayoutInflater, + mPluginManager, + mDumpManager), + mToastLogger); } @Test - public void testStart_addToastUIAsCallbackToCommandQueue() throws Exception { + public void testStart_addToastUIAsCallbackToCommandQueue() { mToastUI.start(); verify(mCommandQueue).addCallback(mToastUI); } @Test - public void testShowToast_addsCorrectViewToWindowManager() throws Exception { + public void testShowToast_addsCorrectViewToWindowManager() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); @@ -138,7 +151,7 @@ public class ToastUITest extends SysuiTestCase { } @Test - public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() throws Exception { + public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); @@ -217,9 +230,14 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_removesView() throws Exception { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); - View view = verifyWmAddViewAndAttachToParent(); + final SystemUIToast toast = mToastUI.mToast; + View view = verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // if applicable, try to finish anim early + } verify(mWindowManager).removeViewImmediate(view); } @@ -228,51 +246,81 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_finishesToken() throws Exception { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // if applicable, try to finish anim early + } verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1); } @Test - public void testHideToast_callsCallback() throws Exception { + public void testHideToast_callsCallback() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); + } verify(mCallback).onToastHidden(); } @Test - public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws Exception { + public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_2); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isFalse(); + } + verify(mCallback, never()).onToastHidden(); } @Test - public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws Exception { + public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_2, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isFalse(); + } + verify(mCallback, never()).onToastHidden(); } @Test - public void testShowToast_afterShowToast_hidesCurrentToast() throws Exception { + public void testShowToast_afterShowToast_hidesCurrentToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); - View view = verifyWmAddViewAndAttachToParent(); + final SystemUIToast toast = mToastUI.mToast; + View view = verifyWmAddViewAndAttachToParent(); mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG, null); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // end early if applicable + } + verify(mWindowManager).removeViewImmediate(view); verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1); verify(mCallback).onToastHidden(); @@ -287,9 +335,48 @@ public class ToastUITest extends SysuiTestCase { } @Test + public void testShowToast_targetsPreS_unlimitedLines_noAppIcon() + throws PackageManager.NameNotFoundException { + // GIVEN the application targets R + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; + when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0, + UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo); + + // WHEN the package posts a toast + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback); + + // THEN the view can have unlimited lines + assertThat(((TextView) mToastUI.mToast.getView() + .findViewById(com.android.systemui.R.id.text)) + .getMaxLines()).isEqualTo(Integer.MAX_VALUE); + } + + @Test + public void testShowToast_targetsS_twoLineLimit_noAppIcon() + throws PackageManager.NameNotFoundException { + // GIVEN the application targets S + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.S; + when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0, + UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo); + + // WHEN the package posts a toast + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback); + + // THEN the view is limited to 2 lines + assertThat(((TextView) mToastUI.mToast.getView() + .findViewById(com.android.systemui.R.id.text)) + .getMaxLines()).isEqualTo(2); + } + + @Test public void testHideToast_logs() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); verify(mToastLogger).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } @@ -298,6 +385,7 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_error_noLog() { // no toast was shown, so this hide is invalid mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + assertThat(mToastUI.mToast).isNull(); verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java index 5fb779adc5be..1aebf1c1c80d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java @@ -127,4 +127,9 @@ public class FakeKeyguardStateController implements KeyguardStateController { public void notifyPanelFlingEnd() { } + + @Override + public boolean canPerformSmartSpaceTransition() { + return false; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java new file mode 100644 index 000000000000..33666bc5b462 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -0,0 +1,158 @@ +/* + * 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 com.android.systemui.wallet.controller; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.service.quickaccesswallet.GetWalletCardsRequest; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.settings.SecureSettings; + +import com.google.common.util.concurrent.MoreExecutors; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class QuickAccessWalletControllerTest extends SysuiTestCase { + + @Mock + private QuickAccessWalletClient mQuickAccessWalletClient; + @Mock + private SecureSettings mSecureSettings; + @Mock + private QuickAccessWalletClient.OnWalletCardsRetrievedCallback mCardsRetriever; + @Captor + private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor; + + private QuickAccessWalletController mController; + private TestableLooper mTestableLooper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true); + + mController = new QuickAccessWalletController( + mContext, + MoreExecutors.directExecutor(), + mSecureSettings, + mQuickAccessWalletClient); + } + + @Test + public void walletEnabled() { + mController.updateWalletPreference(); + + assertTrue(mController.isWalletEnabled()); + } + + @Test + public void walletServiceUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void walletFeatureUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void walletFeatureWhenLockedUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void getWalletClient_NoRecreation_sameClient() { + assertSame(mQuickAccessWalletClient, mController.getWalletClient()); + } + + @Test + public void getWalletClient_reCreateClient_notSameClient() { + mController.reCreateWalletClient(); + + assertNotSame(mQuickAccessWalletClient, mController.getWalletClient()); + } + + @Test + public void queryWalletCards_walletNotEnabled_notQuery() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mController.queryWalletCards(mCardsRetriever); + + verify(mQuickAccessWalletClient, never()).getWalletCards(any(), any(), any()); + } + + @Test + public void queryWalletCards_walletEnabled_queryCards() { + mController.updateWalletPreference(); + mController.queryWalletCards(mCardsRetriever); + + verify(mQuickAccessWalletClient) + .getWalletCards( + eq(MoreExecutors.directExecutor()), + mRequestCaptor.capture(), + eq(mCardsRetriever)); + + GetWalletCardsRequest request = mRequestCaptor.getValue(); + assertEquals(1, mRequestCaptor.getValue().getMaxCards()); + assertEquals( + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width), + request.getCardWidthPx()); + assertEquals( + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), + request.getCardHeightPx()); + } +} diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 04083093d101..71d6a48b89ee 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2569,10 +2569,6 @@ public final class ActiveServices { s.setAllowedBgActivityStartsByBinding(true); } - if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - s.setAllowedBgFgsStartsByBinding(true); - } - if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) { s.isNotAppComponentUsage = true; } @@ -4129,9 +4125,6 @@ public final class ActiveServices { if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.updateIsAllowedBgActivityStartsByBinding(); } - if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - s.updateIsAllowedBgFgsStartsByBinding(); - } if (s.app != null) { updateServiceClientActivitiesLocked(s.app.mServices, c, true); } @@ -5856,8 +5849,6 @@ public final class ActiveServices { final ProcessStateRecord state = app.mState; if (state.isAllowedStartFgsState()) { return getReasonCodeFromProcState(state.getAllowStartFgsState()); - } else if (state.areBackgroundFgsStartsAllowedByToken()) { - return REASON_FGS_BINDING; } else { final ActiveInstrumentation instr = app.getActiveInstrumentation(); if (instr != null diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b44fe9fdaad1..3e6a0a8ec80d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1236,7 +1236,7 @@ public class ActivityManagerService extends IActivityManager.Stub * The temp-allowlist that is allowed to start FGS from background. */ @CompositeRWLock({"this", "mProcLock"}) - final FgsTempAllowList<Integer, FgsTempAllowListItem> mFgsStartTempAllowList = + final FgsTempAllowList<FgsTempAllowListItem> mFgsStartTempAllowList = new FgsTempAllowList(); static final FgsTempAllowListItem FAKE_TEMP_ALLOW_LIST_ITEM = new FgsTempAllowListItem( @@ -1246,7 +1246,7 @@ public class ActivityManagerService extends IActivityManager.Stub * List of uids that are allowed to have while-in-use permission when FGS is started from * background. */ - private final FgsTempAllowList<Integer, String> mFgsWhileInUseTempAllowList = + private final FgsTempAllowList<String> mFgsWhileInUseTempAllowList = new FgsTempAllowList(); /** @@ -9372,22 +9372,17 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mFgsStartTempAllowList:"); final long currentTimeNow = System.currentTimeMillis(); final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); - final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet()); - for (Integer uid : uids) { - final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid); - if (entry == null) { - continue; - } + mFgsStartTempAllowList.forEach((uid, entry) -> { pw.print(" " + UserHandle.formatUid(uid) + ": "); - entry.second.dump(pw); pw.println(); - pw.print("ms expiration="); + entry.second.dump(pw); + pw.print(" expiration="); // Convert entry.mExpirationTime, which is an elapsed time since boot, // to a time since epoch (i.e. System.currentTimeMillis()-based time.) final long expirationInCurrentTime = currentTimeNow - elapsedRealtimeNow + entry.first; TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow); pw.println(); - } + }); } if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -15345,10 +15340,19 @@ public class ActivityManagerService extends IActivityManager.Stub mDeviceIdleTempAllowlist = appids; if (adding) { if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + // Note, the device idle temp-allowlist are by app-ids, but here + // mFgsStartTempAllowList contains UIDs. mFgsStartTempAllowList.add(changingUid, durationMs, new FgsTempAllowListItem(durationMs, reasonCode, reason, callingUid)); } + } else { + // Note in the removing case, we need to remove all the UIDs matching + // the appId, because DeviceIdle's temp-allowlist are based on AppIds, + // not UIDs. + // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will + // not only remove this app for user 0, but for all users. + mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid)); } setAppIdTempAllowlistStateLSP(changingUid, adding); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6cb374a84ad0..4e6e91ac7b5d 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -771,9 +771,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } - // TODO(b/187223764): busTime won't be needed once end_session is a field in BUS. - final long busTime = System.currentTimeMillis(); - final byte[] statsProto = bus.getStatsProto(busTime); + final byte[] statsProto = bus.getStatsProto(); data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto)); diff --git a/services/core/java/com/android/server/am/FgsTempAllowList.java b/services/core/java/com/android/server/am/FgsTempAllowList.java index 847e82f07899..c28655655765 100644 --- a/services/core/java/com/android/server/am/FgsTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsTempAllowList.java @@ -20,11 +20,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.annotation.Nullable; import android.os.SystemClock; -import android.util.ArrayMap; +import android.os.UserHandle; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; -import java.util.Set; +import java.util.function.BiConsumer; /** * List of keys that have expiration time. @@ -33,19 +34,18 @@ import java.util.Set; * * <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p> * - * <p>Note: the underlying data structure is an {@link ArrayMap}, for performance reason, it is only - * suitable to hold up to hundreds of entries.</p> - * @param <K> type of the key. + * <p>Note: the underlying data structure is an {@link SparseArray}, for performance reason, + * it is only suitable to hold up to hundreds of entries.</p> * @param <E> type of the additional optional info. */ -public class FgsTempAllowList<K, E> { +public class FgsTempAllowList<E> { private static final int DEFAULT_MAX_SIZE = 100; /** * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime), * Pair.second is the optional information entry about this key. */ - private final ArrayMap<K, Pair<Long, E>> mTempAllowList = new ArrayMap<>(); + private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>(); private int mMaxSize = DEFAULT_MAX_SIZE; private final Object mLock = new Object(); @@ -70,15 +70,14 @@ public class FgsTempAllowList<K, E> { /** * Add a key and its duration with optional info into the temp allowlist. - * @param key * @param durationMs temp-allowlisted duration in milliseconds. * @param entry additional optional information of this key, could be null. */ - public void add(K key, long durationMs, @Nullable E entry) { + public void add(int uid, long durationMs, @Nullable E entry) { synchronized (mLock) { if (durationMs <= 0) { Slog.e(TAG_AM, "FgsTempAllowList bad duration:" + durationMs + " key: " - + key); + + uid); return; } // The temp allowlist should be a short list with only a few entries in it. @@ -94,10 +93,10 @@ public class FgsTempAllowList<K, E> { } } } - final Pair<Long, E> existing = mTempAllowList.get(key); + final Pair<Long, E> existing = mTempAllowList.get(uid); final long expirationTime = now + durationMs; if (existing == null || existing.first < expirationTime) { - mTempAllowList.put(key, new Pair(expirationTime, entry)); + mTempAllowList.put(uid, new Pair(expirationTime, entry)); } } } @@ -105,13 +104,12 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return its non-null value. * If the key has expired, return null. - * @param key * @return */ @Nullable - public Pair<Long, E> get(K key) { + public Pair<Long, E> get(int uid) { synchronized (mLock) { - final int index = mTempAllowList.indexOfKey(key); + final int index = mTempAllowList.indexOfKey(uid); if (index < 0) { return null; } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) { @@ -126,23 +124,48 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return true. * If the key has expired, return false. - * @param key - * @return */ - public boolean isAllowed(K key) { - Pair<Long, E> entry = get(key); + public boolean isAllowed(int uid) { + Pair<Long, E> entry = get(uid); return entry != null; } - public void remove(K key) { + /** + * Remove a given UID. + */ + public void removeUid(int uid) { synchronized (mLock) { - mTempAllowList.remove(key); + mTempAllowList.remove(uid); } } - public Set<K> keySet() { + /** + * Remove by appId. + */ + public void removeAppId(int appId) { synchronized (mLock) { - return mTempAllowList.keySet(); + // Find all UIDs matching the appId. + for (int i = mTempAllowList.size() - 1; i >= 0; i--) { + final int uid = mTempAllowList.keyAt(i); + if (UserHandle.getAppId(uid) == appId) { + mTempAllowList.removeAt(i); + } + } + } + } + + /** + * Iterate over the entries. + */ + public void forEach(BiConsumer<Integer, Pair<Long, E>> callback) { + synchronized (mLock) { + for (int i = 0; i < mTempAllowList.size(); i++) { + final int uid = mTempAllowList.keyAt(i); + final Pair<Long, E> entry = mTempAllowList.valueAt(i); + if (entry != null) { + callback.accept(uid, entry); + } + } } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0ffaccfa0e89..457fe0f88aa7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3074,7 +3074,7 @@ public final class ProcessList { UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); - mService.mFgsStartTempAllowList.remove(record.info.uid); + mService.mFgsStartTempAllowList.removeUid(record.info.uid); mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT, ActivityManager.PROCESS_CAPABILITY_NONE); } diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index d83e13cb2e41..1fb5572cc5fe 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -26,7 +26,6 @@ import static com.android.server.am.ProcessRecord.TAG; import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; -import android.os.Binder; import android.os.SystemClock; import android.util.ArraySet; import android.util.Slog; @@ -302,9 +301,6 @@ final class ProcessStateRecord { @GuardedBy("mService") private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; - @GuardedBy("mService") - private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>(); - /** * Whether or not this process has been in forced-app-standby state. */ @@ -1101,21 +1097,6 @@ final class ProcessStateRecord { } @GuardedBy("mService") - void addAllowBackgroundFgsStartsToken(Binder entity) { - mBackgroundFgsStartTokens.add(entity); - } - - @GuardedBy("mService") - void removeAllowBackgroundFgsStartsToken(Binder entity) { - mBackgroundFgsStartTokens.remove(entity); - } - - @GuardedBy("mService") - boolean areBackgroundFgsStartsAllowedByToken() { - return !mBackgroundFgsStartTokens.isEmpty(); - } - - @GuardedBy("mService") void resetAllowStartFgsState() { mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index fd59e852ddd7..dbb2f65b0680 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -149,10 +149,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN @GuardedBy("ams") private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>(); - // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND - // flag? if true, the process can start FGS from background. - boolean mIsAllowedBgFgsStartsByBinding; - // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; @@ -445,10 +441,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart="); pw.println(mIsAllowedBgActivityStartsByStart); } - if (mIsAllowedBgFgsStartsByBinding) { - pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding="); - pw.println(mIsAllowedBgFgsStartsByBinding); - } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); @@ -634,11 +626,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } else { proc.removeAllowBackgroundActivityStartsToken(this); } - if (mIsAllowedBgFgsStartsByBinding) { - proc.mState.addAllowBackgroundFgsStartsToken(this); - } else { - proc.mState.removeAllowBackgroundFgsStartsToken(this); - } } if (app != null && app != proc) { // If the old app is allowed to start bg activities because of a service start, leave it @@ -726,34 +713,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN setAllowedBgActivityStartsByBinding(isAllowedByBinding); } - void updateIsAllowedBgFgsStartsByBinding() { - boolean isAllowedByBinding = false; - for (int conni = connections.size() - 1; conni >= 0; conni--) { - ArrayList<ConnectionRecord> cr = connections.valueAt(conni); - for (int i = 0; i < cr.size(); i++) { - if ((cr.get(i).flags - & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - isAllowedByBinding = true; - break; - } - } - if (isAllowedByBinding) { - break; - } - } - setAllowedBgFgsStartsByBinding(isAllowedByBinding); - } - void setAllowedBgActivityStartsByBinding(boolean newValue) { mIsAllowedBgActivityStartsByBinding = newValue; updateParentProcessBgActivityStartsToken(); } - void setAllowedBgFgsStartsByBinding(boolean newValue) { - mIsAllowedBgFgsStartsByBinding = newValue; - updateParentProcessBgFgsStartsToken(); - } - /** * Called when the service is started with allowBackgroundActivityStarts set. We allow * it for background activity starts, setting up a callback to remove this ability after a @@ -846,17 +810,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } - private void updateParentProcessBgFgsStartsToken() { - if (app == null) { - return; - } - if (mIsAllowedBgFgsStartsByBinding) { - app.mState.addAllowBackgroundFgsStartsToken(this); - } else { - app.mState.removeAllowBackgroundFgsStartsToken(this); - } - } - /** * Returns the originating token if that's the only reason background activity starts are * allowed. In order for that to happen the service has to be allowed only due to starts, since diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 3f2b8fffcc54..b714c6d73613 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -33,6 +33,9 @@ import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; @@ -126,6 +129,7 @@ class RebootEscrowManager { ERROR_UNLOCK_ALL_USERS, ERROR_PROVIDER_MISMATCH, ERROR_KEYSTORE_FAILURE, + ERROR_NO_NETWORK, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -139,6 +143,7 @@ class RebootEscrowManager { static final int ERROR_UNLOCK_ALL_USERS = 5; static final int ERROR_PROVIDER_MISMATCH = 6; static final int ERROR_KEYSTORE_FAILURE = 7; + static final int ERROR_NO_NETWORK = 8; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -235,6 +240,23 @@ class RebootEscrowManager { "server_based_ror_enabled", false); } + public boolean isNetworkConnected() { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return false; + } + + Network activeNetwork = connectivityManager.getActiveNetwork(); + NetworkCapabilities networkCapabilities = + connectivityManager.getNetworkCapabilities(activeNetwork); + return networkCapabilities != null + && networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_INTERNET) + && networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + public Context getContext() { return mContext; } @@ -363,7 +385,11 @@ class RebootEscrowManager { } Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); - mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; + if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { + mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; + } else { + mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; + } onGetRebootEscrowKeyFailed(users, attemptNumber); } @@ -471,6 +497,8 @@ class RebootEscrowManager { mLoadEscrowDataErrorCode = ERROR_UNKNOWN; } + Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: " + + serviceType + ", error code: " + mLoadEscrowDataErrorCode); // TODO(179105110) report the duration since boot complete. mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, -1); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f0e693976faa..0dd9b292b386 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4221,6 +4221,7 @@ public class NotificationManagerService extends SystemService { } } + /** Notifications returned here will have allowlistToken stripped from them. */ private StatusBarNotification sanitizeSbn(String pkg, int userId, StatusBarNotification sbn) { if (sbn.getUserId() == userId) { @@ -4228,11 +4229,16 @@ public class NotificationManagerService extends SystemService { // We could pass back a cloneLight() but clients might get confused and // try to send this thing back to notify() again, which would not work // very well. + Notification notification = sbn.getNotification().clone(); + // Remove background token before returning notification to untrusted app, this + // ensures the app isn't able to perform background operations that are + // associated with notification interactions. + notification.setAllowlistToken(null); return new StatusBarNotification( sbn.getPackageName(), sbn.getOpPkg(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), - sbn.getNotification().clone(), + notification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); } } diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index 946f8d52ae43..83d4ce7ff057 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -22,8 +22,8 @@ import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILATI import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; -import android.util.jar.StrictJarFile; import android.util.Slog; +import android.util.jar.StrictJarFile; import com.android.internal.art.ArtStatsLog; import com.android.server.pm.PackageManagerService; @@ -163,7 +163,7 @@ public class ArtStatsLogUtils { uid, compilationReason, compilerFilter, - ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_COUNTER_BYTES, getDexBytes(apkPath), dexMetadataType, apkType, @@ -173,7 +173,7 @@ public class ArtStatsLogUtils { uid, compilationReason, compilerFilter, - ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME_COUNTER_MILLIS, compileTime, dexMetadataType, apkType, diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 983b5b17bb92..9c25159faef1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -192,6 +192,7 @@ import android.view.autofill.AutofillManagerInternal; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.app.AssistUtils; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -650,7 +651,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LAUNCH_ASSIST: final int deviceId = msg.arg1; final Long eventTime = (Long) msg.obj; - launchAssistAction(null /* hint */, deviceId, eventTime); + launchAssistAction(null /* hint */, deviceId, eventTime, + AssistUtils.INVOCATION_TYPE_UNKNOWN); break; case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: launchVoiceAssistWithWakeLock(); @@ -1108,7 +1110,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false, "Power - Long Press - Go To Assistant"); final int powerKeyDeviceId = Integer.MIN_VALUE; - launchAssistAction(null, powerKeyDeviceId, eventTime); + launchAssistAction(null, powerKeyDeviceId, eventTime, + AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS); break; } } @@ -1541,7 +1544,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { launchAllAppsAction(); break; case LONG_PRESS_HOME_ASSIST: - launchAssistAction(null, deviceId, eventTime); + launchAssistAction(null, deviceId, eventTime, + AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); break; case LONG_PRESS_HOME_NOTIFICATION_PANEL: toggleNotificationPanel(); @@ -2772,7 +2776,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (mPendingMetaAction) { launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId(), - event.getEventTime()); + event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN); mPendingMetaAction = false; } } @@ -3036,7 +3040,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // various parts of the UI. /** Asks the status bar to startAssist(), usually a full "assistant" interface */ - private void launchAssistAction(String hint, int deviceId, long eventTime) { + private void launchAssistAction(String hint, int deviceId, long eventTime, + int invocationType) { sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); if (!isUserSetupComplete()) { // Disable opening assist window during setup @@ -3053,6 +3058,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { args.putBoolean(hint, true); } args.putLong(Intent.EXTRA_TIME, eventTime); + args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType); ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0) .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index a82c91e2352b..dc868b325900 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -126,6 +126,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.connectivity.WifiActivityEnergyInfo; +import android.os.incremental.IncrementalManager; import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; @@ -426,6 +427,7 @@ public class StatsPullAtomService extends SystemService { private final Object mHealthHalLock = new Object(); private final Object mAttributedAppOpsLock = new Object(); private final Object mSettingsStatsLock = new Object(); + private final Object mInstalledIncrementalPackagesLock = new Object(); public StatsPullAtomService(Context context) { super(context); @@ -695,6 +697,10 @@ public class StatsPullAtomService extends SystemService { synchronized (mSettingsStatsLock) { return pullSettingsStatsLocked(atomTag, data); } + case FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE: + synchronized (mInstalledIncrementalPackagesLock) { + return pullInstalledIncrementalPackagesLocked(atomTag, data); + } default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -877,6 +883,7 @@ public class StatsPullAtomService extends SystemService { registerBatteryVoltage(); registerBatteryCycleCount(); registerSettingsStats(); + registerInstalledIncrementalPackages(); } private void initAndRegisterNetworkStatsPullers() { @@ -3949,6 +3956,31 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerInstalledIncrementalPackages() { + int tagId = FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE; + mStatsManager.setPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullInstalledIncrementalPackagesLocked(int atomTag, List<StatsEvent> pulledData) { + final PackageManager pm = mContext.getPackageManager(); + if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) { + // Incremental is not enabled on this device. The result list will be empty. + return StatsManager.PULL_SUCCESS; + } + List<PackageInfo> installedPackages = pm.getInstalledPackages(0); + for (PackageInfo pi : installedPackages) { + if (IncrementalManager.isIncrementalPath(pi.applicationInfo.getBaseCodePath())) { + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid)); + } + } + return StatsManager.PULL_SUCCESS; + } + // Thermal event received from vendor thermal management subsystem private static final class ThermalEventListener extends IThermalEventListener.Stub { @Override diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0bc3d4a461df..38966b93681c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -181,7 +181,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED; import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.IdentifierProto.HASH_CODE; @@ -5359,11 +5358,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.deferWindowLayout(); try { task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */); - // If there is no possible transition to execute, then allow to skip layout - // because it may be done by next resumed activity. - if (!pausingActivity.mDisplayContent.areOpeningAppsReady()) { - mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_ALLOWED); - } } finally { mAtmService.continueWindowLayout(); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 8262c59945c4..66e55e04d198 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1549,8 +1549,6 @@ class ActivityStarter { mService.getTransitionController().collect(r); try { mService.deferWindowLayout(); - // Allow to skip layout because it may be done by the window of the starting activity. - mService.addWindowLayoutReasons(ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED); Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 57c2a1387ce2..134ecdeffa8f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -657,27 +657,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @IntDef({ LAYOUT_REASON_CONFIG_CHANGED, LAYOUT_REASON_VISIBILITY_CHANGED, - SKIP_LAYOUT_REASON_ALLOWED, - SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT, }) @interface LayoutReason { } - static final int LAYOUT_REASON_MASK = 0x0000ffff; static final int LAYOUT_REASON_CONFIG_CHANGED = 0x1; static final int LAYOUT_REASON_VISIBILITY_CHANGED = 0x2; - static final int SKIP_LAYOUT_REASON_MASK = 0xfffe0000; - /** - * Allow to call {@link WindowSurfacePlacer#endDeferAndSkipLayout} if there is a reason - * included in {@link #SKIP_LAYOUT_REASON_MASK} was added. - */ - static final int SKIP_LAYOUT_REASON_ALLOWED = 1 << 16; - - /** Used when the client is scheduled to call relayout. */ - static final int SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT = 1 << 17; - - /** The reasons to perform or skip surface placement. */ + /** The reasons to perform surface placement. */ @LayoutReason private int mLayoutReasons; @@ -4205,35 +4192,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager.mWindowPlacerLocked.deferLayout(); } - /** - * @see WindowSurfacePlacer#continueLayout - * @see WindowSurfacePlacer#endDeferAndSkipLayout - */ + /** @see WindowSurfacePlacer#continueLayout */ void continueWindowLayout() { - if ((mLayoutReasons & SKIP_LAYOUT_REASON_ALLOWED) != 0) { - final int skipReasons = mLayoutReasons & SKIP_LAYOUT_REASON_MASK; - if (skipReasons != 0) { - if (DEBUG_ALL) { - Slog.i(TAG, "continueWindowLayout skip-reasons=" - + Integer.toHexString(skipReasons)); - } - mWindowManager.mWindowPlacerLocked.endDeferAndSkipLayout(); - return; - } - } - - final int reasons = mLayoutReasons & LAYOUT_REASON_MASK; - mWindowManager.mWindowPlacerLocked.continueLayout(reasons != 0); - if (DEBUG_ALL && reasons != 0 && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { - Slog.i(TAG, "continueWindowLayout reasons=" + Integer.toHexString(reasons)); + mWindowManager.mWindowPlacerLocked.continueLayout(mLayoutReasons != 0); + if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { + Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons); } } /** - * If a reason within {@link #LAYOUT_REASON_MASK} is added between {@link #deferWindowLayout} - * and {@link #continueWindowLayout}, {@link WindowSurfacePlacer#performSurfacePlacement} will - * be called when the last defer count is gone. Note that the {@link #SKIP_LAYOUT_REASON_MASK} - * has higher priority to determine whether to perform layout. + * If a reason is added between {@link #deferWindowLayout} and {@link #continueWindowLayout}, + * it will make sure {@link WindowSurfacePlacer#performSurfacePlacement} is called when the last + * defer count is gone. */ void addWindowLayoutReasons(@LayoutReason int reasons) { mLayoutReasons |= reasons; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index b1404514f1f5..7fe0f5be287c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -938,9 +938,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (r.app != null) { r.app.updateServiceConnectionActivities(); } - // Expect a window of the starting activity will perform relayout. - mService.addWindowLayoutReasons( - ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT); return true; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 13fd76c5951b..306137a313f7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4035,22 +4035,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mLayoutNeeded; } - /** Returns {@code true} if all opening apps may be ready to execute transition. */ - boolean areOpeningAppsReady() { - final int size = mOpeningApps.size(); - for (int i = size - 1; i >= 0; i--) { - final ActivityRecord r = mOpeningApps.valueAt(i); - if (!r.hasVisible) { - return false; - } - final WindowState w = r.findMainWindow(); - if (w != null && !w.isDrawn()) { - return false; - } - } - return size > 0; - } - void dumpTokens(PrintWriter pw, boolean dumpAll) { if (mTokenMap.isEmpty()) { return; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 2330482bbfe7..039422d2c6a4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -106,7 +106,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIB import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG; -import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; @@ -6289,8 +6288,6 @@ class Task extends WindowContainer<WindowContainer> { if (lastResumed != null) { lastResumed.setWillCloseOrEnterPip(true); } - // There may be a relayout from resuming next activity after the previous is paused. - mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT); return true; } else if (mResumedActivity == next && next.isState(RESUMED) && taskDisplayArea.allResumedActivitiesComplete()) { @@ -6500,7 +6497,6 @@ class Task extends WindowContainer<WindowContainer> { ResumeActivityItem.obtain(next.app.getReportedProcState(), dc.isNextTransitionForward())); mAtmService.getLifecycleManager().scheduleTransaction(transaction); - mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT); ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next); } catch (Exception e) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 2d2b1f4ad322..2ee5fb01efb3 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -101,16 +101,6 @@ class WindowSurfacePlacer { } } - /** - * Resumes layout passes but skip to perform layout even if there was a request. This can only - * be called when there will be another layout request from client side, e.g. an activity is - * starting or resuming. And there should be no other significant changes need to apply. - */ - void endDeferAndSkipLayout() { - mDeferDepth--; - mDeferredRequests = 0; - } - boolean isLayoutDeferred() { return mDeferDepth > 0; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 68cb8f94aa0a..3dc7bc1154ea 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -48,14 +48,18 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; import android.os.Build; import android.os.Looper; import android.os.SystemClock; @@ -70,6 +74,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -83,6 +88,8 @@ public class ConnectivityControllerTest { @Mock private Context mContext; @Mock + private BatteryManagerInternal mBatteryManagerInternal; + @Mock private ConnectivityManager mConnManager; @Mock private NetworkPolicyManager mNetPolicyManager; @@ -108,6 +115,9 @@ public class ConnectivityControllerTest { LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); + LocalServices.removeServiceForTest(BatteryManagerInternal.class); + LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal); + when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); // Freeze the clocks at this moment in time @@ -143,8 +153,18 @@ public class ConnectivityControllerTest { DataUnit.MEBIBYTES.toBytes(1)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + final ArgumentCaptor<BroadcastReceiver> chargingCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(false); final ConnectivityController controller = new ConnectivityController(mService); + verify(mContext).registerReceiver(chargingCaptor.capture(), + ArgumentMatchers.argThat(filter -> + filter.hasAction(BatteryManager.ACTION_CHARGING) + && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L); + final BroadcastReceiver chargingReceiver = chargingCaptor.getValue(); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, @@ -166,7 +186,18 @@ public class ConnectivityControllerTest { assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); + // Slow network is too slow, but device is charging and network is unmetered. + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(true); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + assertTrue(controller.isSatisfied(createJobStatus(job), net, + createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) + .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), + mConstants)); + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(false); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L); // Slow network is too slow @@ -189,6 +220,14 @@ public class ConnectivityControllerTest { assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); + // Slow network is too slow, but device is charging and network is unmetered. + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(true); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + assertTrue(controller.isSatisfied(createJobStatus(job), net, + createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) + .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), + mConstants)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java index f85f0f843789..50d4d8475395 100644 --- a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java +++ b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java @@ -29,6 +29,9 @@ import android.util.Pair; import org.junit.Test; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + /** * Build/Install/Run: * atest FrameworksServicesTests:TempAllowListTest @@ -41,7 +44,7 @@ public class FgsTempAllowListTest { */ @Test public void testIsAllowed() { - FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); + FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); @@ -55,7 +58,7 @@ public class FgsTempAllowListTest { assertNotNull(entry2); assertEquals(entry2.second, "description2"); - allowList.remove(10001); + allowList.removeUid(10001); assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } @@ -65,7 +68,7 @@ public class FgsTempAllowListTest { */ @Test public void testExpired() { - FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); + FgsTempAllowList<String> allowList = new FgsTempAllowList(); // temp allow for 2000ms. allowList.add(10001, 2000, "uid1-2000ms"); // sleep for 3000ms. @@ -74,4 +77,51 @@ public class FgsTempAllowListTest { assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } + + @Test + public void testRemoveAppId() { + FgsTempAllowList<String> allowList = new FgsTempAllowList(); + allowList.add(10001, 2000, "description1"); + allowList.add(10002, 2000, "description2"); + allowList.add(10_10001, 2000, "description3"); + + assertTrue(allowList.isAllowed(10001)); + assertTrue(allowList.isAllowed(10002)); + assertTrue(allowList.isAllowed(10_10001)); + + allowList.removeAppId(10001); + + assertFalse(allowList.isAllowed(10001)); + assertTrue(allowList.isAllowed(10002)); + assertFalse(allowList.isAllowed(10_10001)); + } + + @Test + public void testForEach() { + final FgsTempAllowList<String> allowList = new FgsTempAllowList(); + + + // Call forEach(), return the sum of all the UIDs, and make sure the item is + // "uid" + uid. + final Supplier<Integer> callForEach = () -> { + final AtomicInteger sum = new AtomicInteger(); + sum.set(0); + allowList.forEach((uid, entry) -> { + sum.set(sum.get() + uid); + assertEquals(entry.second, "uid" + uid); + }); + return sum.get(); + }; + + // Call on th empty list. + assertEquals(0, (int) callForEach.get()); + + // Add one item. + allowList.add(1, 2000, "uid1"); + assertEquals(1, (int) callForEach.get()); + + // Add one more item. + allowList.add(10, 2000, "uid10"); + assertEquals(11, (int) callForEach.get()); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index aecc7942b266..b01c1c8ead28 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -165,7 +165,17 @@ public class RebootEscrowManagerTests { mRebootEscrow = null; mServerBased = true; RebootEscrowProviderServerBasedImpl.Injector injector = - new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection); + new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) { + @Override + long getServiceTimeoutInSeconds() { + return 30; + } + + @Override + long getServerBlobLifetimeInMillis() { + return 600_000; + } + }; mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl( storage, injector); mUserManager = userManager; @@ -189,6 +199,11 @@ public class RebootEscrowManagerTests { } @Override + public boolean isNetworkConnected() { + return false; + } + + @Override public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() { mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider; return mRebootEscrowProviderInUse; @@ -602,7 +617,7 @@ public class RebootEscrowManagerTests { // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), metricsErrorCodeCaptor.getValue()); } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java index ef5f00bf8a3c..fdb6e9f5f4ce 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java @@ -28,8 +28,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.mockito.Mock; import org.mockito.InOrder; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.IOException; @@ -277,7 +277,7 @@ public final class ArtStatsLogUtilsTest { UID, COMPILATION_REASON, COMPILER_FILTER, - ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_COUNTER_BYTES, DEX_CONTENT.length, dexMetadataType, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, @@ -287,7 +287,7 @@ public final class ArtStatsLogUtilsTest { UID, COMPILATION_REASON, COMPILER_FILTER, - ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, + ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME_COUNTER_MILLIS, COMPILE_TIME, dexMetadataType, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c527e66bae29..6b7fc2fb4b7d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3682,6 +3682,13 @@ public class CarrierConfigManager { "emergency_number_prefix_string_array"; /** + * Indicates whether carrier treats "*67" or "*82" as a temporary mode CLIR. + * @hide + */ + public static final String KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL = + "carrier_supports_caller_id_vertical_service_codes_bool"; + + /** * Smart forwarding config. Smart forwarding is a feature to configure call forwarding to a * different SIM in the device when one SIM is not reachable. The config here specifies a smart * forwarding component that will launch UI for changing the configuration. An empty string @@ -5496,6 +5503,7 @@ public class CarrierConfigManager { 1 /* Roaming Indicator Off */ }); sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]); + sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, false); sDefaults.putBoolean(KEY_USE_USIM_BOOL, false); sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false); sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true); diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index 2b4fb7d5cbf3..734b52018857 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -79,7 +79,7 @@ public interface DelegateStateCallback { * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration * change event to reduce conditions where the remote application is using a stale IMS * configuration. - * @deprecated This is being removed from API surface, Use + * @removed This is being removed from API surface, Use * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead. */ @Deprecated diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index 08513c23291a..fe14dd18d1ab 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -34,7 +34,7 @@ import java.net.InetSocketAddress; /** * @hide - * @deprecated Use {@link SipDelegateConfiguration} instead. + * @removed Use {@link SipDelegateConfiguration} instead. */ @Deprecated @SystemApi diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java index c078637e3791..42c53f2a0caf 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -136,7 +136,7 @@ public interface DelegateConnectionStateCallback { * not compleed yet. * * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. - * @deprecated Will not be in final API, use + * @removed Will not be in final API, use * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}. */ @Deprecated @@ -161,7 +161,7 @@ public interface DelegateConnectionStateCallback { * * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. */ - default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {} + void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig); /** * The previously created {@link SipDelegateConnection} instance delivered via diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 91ecbf081919..a096c1f3b8b2 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2440,6 +2440,12 @@ interface ITelephony { boolean removeUceRequestDisallowedStatus(int subId); /** + * Set the timeout for contact capabilities request. + * Note: This is designed for a SHELL command only. + */ + boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfterMs); + + /** * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the * specified thresholds. */ diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py new file mode 100644 index 000000000000..297d9c3bcca0 --- /dev/null +++ b/tools/apilint/deprecated_at_birth.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python + +# 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. + +""" +Usage: deprecated_at_birth.py path/to/next/ path/to/previous/ +Usage: deprecated_at_birth.py prebuilts/sdk/31/public/api/ prebuilts/sdk/30/public/api/ +""" + +import re, sys, os, collections, traceback, argparse + + +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + +def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): + # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes + codes = [] + if reset: codes.append("0") + else: + if not fg is None: codes.append("3%d" % (fg)) + if not bg is None: + if not bright: codes.append("4%d" % (bg)) + else: codes.append("10%d" % (bg)) + if bold: codes.append("1") + elif dim: codes.append("2") + else: codes.append("22") + return "\033[%sm" % (";".join(codes)) + + +def ident(raw): + """Strips superficial signature changes, giving us a strong key that + can be used to identify members across API levels.""" + raw = raw.replace(" deprecated ", " ") + raw = raw.replace(" synchronized ", " ") + raw = raw.replace(" final ", " ") + raw = re.sub("<.+?>", "", raw) + raw = re.sub("@[A-Za-z]+ ", "", raw) + raw = re.sub("@[A-Za-z]+\(.+?\) ", "", raw) + if " throws " in raw: + raw = raw[:raw.index(" throws ")] + return raw + + +class Field(): + def __init__(self, clazz, line, raw, blame): + self.clazz = clazz + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + raw = raw.split() + self.split = list(raw) + + raw = [ r for r in raw if not r.startswith("@") ] + for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1].strip(";") + if len(raw) >= 4 and raw[2] == "=": + self.value = raw[3].strip(';"') + else: + self.value = None + self.ident = ident(self.raw) + + def __hash__(self): + return hash(self.raw) + + def __repr__(self): + return self.raw + + +class Method(): + def __init__(self, clazz, line, raw, blame): + self.clazz = clazz + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + # drop generics for now + raw = re.sub("<.+?>", "", raw) + + raw = re.split("[\s(),;]+", raw) + for r in ["", ";"]: + while r in raw: raw.remove(r) + self.split = list(raw) + + raw = [ r for r in raw if not r.startswith("@") ] + for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1] + self.args = [] + self.throws = [] + target = self.args + for r in raw[2:]: + if r == "throws": target = self.throws + else: target.append(r) + self.ident = ident(self.raw) + + def __hash__(self): + return hash(self.raw) + + def __repr__(self): + return self.raw + + +class Class(): + def __init__(self, pkg, line, raw, blame): + self.pkg = pkg + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + self.ctors = [] + self.fields = [] + self.methods = [] + + raw = raw.split() + self.split = list(raw) + if "class" in raw: + self.fullname = raw[raw.index("class")+1] + elif "enum" in raw: + self.fullname = raw[raw.index("enum")+1] + elif "interface" in raw: + self.fullname = raw[raw.index("interface")+1] + elif "@interface" in raw: + self.fullname = raw[raw.index("@interface")+1] + else: + raise ValueError("Funky class type %s" % (self.raw)) + + if "extends" in raw: + self.extends = raw[raw.index("extends")+1] + self.extends_path = self.extends.split(".") + else: + self.extends = None + self.extends_path = [] + + self.fullname = self.pkg.name + "." + self.fullname + self.fullname_path = self.fullname.split(".") + + self.name = self.fullname[self.fullname.rindex(".")+1:] + + def __hash__(self): + return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods))) + + def __repr__(self): + return self.raw + + +class Package(): + def __init__(self, line, raw, blame): + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + raw = raw.split() + self.name = raw[raw.index("package")+1] + self.name_path = self.name.split(".") + + def __repr__(self): + return self.raw + + +def _parse_stream(f, api={}): + line = 0 + pkg = None + clazz = None + blame = None + + re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") + for raw in f: + line += 1 + raw = raw.rstrip() + match = re_blame.match(raw) + if match is not None: + blame = match.groups()[0:2] + raw = match.groups()[2] + else: + blame = None + + if raw.startswith("package"): + pkg = Package(line, raw, blame) + elif raw.startswith(" ") and raw.endswith("{"): + clazz = Class(pkg, line, raw, blame) + api[clazz.fullname] = clazz + elif raw.startswith(" ctor"): + clazz.ctors.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" method"): + clazz.methods.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" field"): + clazz.fields.append(Field(clazz, line, raw, blame)) + + return api + + +def _parse_stream_path(path): + api = {} + print "Parsing", path + for f in os.listdir(path): + f = os.path.join(path, f) + if not os.path.isfile(f): continue + if not f.endswith(".txt"): continue + if f.endswith("removed.txt"): continue + print "\t", f + with open(f) as s: + api = _parse_stream(s, api) + print "Parsed", len(api), "APIs" + print + return api + + +class Failure(): + def __init__(self, sig, clazz, detail, error, rule, msg): + self.sig = sig + self.error = error + self.rule = rule + self.msg = msg + + if error: + self.head = "Error %s" % (rule) if rule else "Error" + dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg) + else: + self.head = "Warning %s" % (rule) if rule else "Warning" + dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg) + + self.line = clazz.line + blame = clazz.blame + if detail is not None: + dump += "\n in " + repr(detail) + self.line = detail.line + blame = detail.blame + dump += "\n in " + repr(clazz) + dump += "\n in " + repr(clazz.pkg) + dump += "\n at line " + repr(self.line) + if blame is not None: + dump += "\n last modified by %s in %s" % (blame[1], blame[0]) + + self.dump = dump + + def __repr__(self): + return self.dump + + +failures = {} + +def _fail(clazz, detail, error, rule, msg): + """Records an API failure to be processed later.""" + global failures + + sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) + sig = sig.replace(" deprecated ", " ") + + failures[sig] = Failure(sig, clazz, detail, error, rule, msg) + + +def warn(clazz, detail, rule, msg): + _fail(clazz, detail, False, rule, msg) + +def error(clazz, detail, rule, msg): + _fail(clazz, detail, True, rule, msg) + + +if __name__ == "__main__": + next_path = sys.argv[1] + prev_path = sys.argv[2] + + next_api = _parse_stream_path(next_path) + prev_api = _parse_stream_path(prev_path) + + # Remove all existing things so we're left with new + for prev_clazz in prev_api.values(): + if prev_clazz.fullname not in next_api: continue + cur_clazz = next_api[prev_clazz.fullname] + + sigs = { i.ident: i for i in prev_clazz.ctors } + cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.methods } + cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.fields } + cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ] + + # Forget about class entirely when nothing new + if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0: + del next_api[prev_clazz.fullname] + + for clazz in next_api.values(): + if "@Deprecated " in clazz.raw and not clazz.fullname in prev_api: + error(clazz, None, None, "Found API deprecation at birth") + + if "@Deprecated " in clazz.raw: continue + + for i in clazz.ctors + clazz.methods + clazz.fields: + if "@Deprecated " in i.raw: + error(clazz, i, None, "Found API deprecation at birth " + i.ident) + + print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), + format(reset=True))) + for f in sorted(failures): + print failures[f] + print diff --git a/wifi/OWNERS b/wifi/OWNERS index 2caf9ed7a38a..8381522231de 100644 --- a/wifi/OWNERS +++ b/wifi/OWNERS @@ -1,5 +1,3 @@ set noparent -dysu@google.com -etancohen@google.com -satk@google.com +include platform/packages/modules/Wifi:/WIFI_OWNERS |