Merge "Update boot image and system server profiles [M5C5P89S0PP]" into sc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b3c33b6..144536e 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 @@
* </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 60f5769..cef065d 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 @@
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 5365218..1169391 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.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 @@
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 @@
.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 @@
}
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 @@
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 @@
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 @@
// 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 7b947fd..e8065aa 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 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 @@
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 @@
// 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 @@
*/
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 @@
* {@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 @@
}
@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 @@
}
}
+ 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 002b333..4c61afe 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31964,7 +31964,6 @@
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 @@
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 f23966e..7072bbe 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4930,10 +4930,6 @@
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 @@
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 @@
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 @@
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 @@
}
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 7cf0076..9a8a493 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -135,6 +135,7 @@
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.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 8499b37..2f795f0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1360,10 +1360,6 @@
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 8d39d8a..7149096 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
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 @@
*/
@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 @@
// 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 @@
}
@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 @@
// 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 @@
// 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 @@
}
}
- @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 c752f34..115101c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -82,9 +82,6 @@
/** 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 @@
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 47ababf..432d99d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3314,6 +3314,19 @@
}
/**
+ * 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 d5585db..032b57e 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,7 +40,8 @@
@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 f7d7e9d..a539812 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -55,15 +55,40 @@
@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 e281a02..34e4fcd 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -82,7 +82,12 @@
@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 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 @@
@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 944367e..4b8a347 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,7 +40,8 @@
@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 fc676cf..886cd7f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -400,10 +400,7 @@
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 33606e8..33a34be 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4672,27 +4672,27 @@
@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 f0d410f..881e0cf 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -481,6 +481,7 @@
boolean mFullscreenApplied;
boolean mIsFullscreen;
+ private boolean mLastWasInFullscreenMode;
@UnsupportedAppUsage
View mExtractView;
boolean mExtractViewHidden;
@@ -920,8 +921,17 @@
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 @@
onWindowHidden();
mDecorViewWasVisible = false;
}
+ mLastWasInFullscreenMode = mIsFullscreen;
updateFullscreenMode();
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 370052d..f2857ce 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -368,27 +368,19 @@
};
/** 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 @@
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 5848d2f..224cd84 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -189,28 +189,6 @@
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 @@
}
/**
- * 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 01cece3..c82a516 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -94,6 +94,8 @@
* 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 @@
* 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 5cfb665..e3302d1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9850,12 +9850,10 @@
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 0acacb6..908d236 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9828,30 +9828,37 @@
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 @@
}
+ 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 0d041c4..e693d9d 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -201,7 +201,10 @@
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 a26abc2..b4a2b63 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -40,8 +40,10 @@
// 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 @@
@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 @@
// 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 @@
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 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 @@
}
}
- 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 @@
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 084d4d1..f457c56 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 bee0a0b..333eebb 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 @@
@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 @@
}
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 cd45060..a36d9fe 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 @@
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 @@
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 @@
.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 @@
.isWithin(PRECISION).of(18.888888);
}
- private static class MockKernelCpuUidFreqTimeReader extends
- KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
- private long[] 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]);
- MockKernelCpuUidFreqTimeReader() {
- super(/*throttle */false);
- }
+ prepareBatteryStats(new long[]{50000000, 100000000});
- void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
- mSystemServerCpuTimes = systemServerCpuTimes;
- }
+ SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
+ mStatsRule.getPowerProfile());
- @Override
- public boolean perClusterTimesAvailable() {
- return true;
- }
+ mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
- @Override
- public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
- if (cb != null) {
- cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
- }
- }
+ 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 MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
- private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+ private void prepareBatteryStats(long[] clusterChargesUc) {
+ when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
- MockSystemServerCpuThreadReader() {
- super(null);
- }
+ when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+ when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
- public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
- mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
- mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
- }
+ when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
- @Override
- public SystemServiceCpuThreadTimes readDelta() {
- return mThreadTimes;
- }
- }
+ // 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());
- 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);
- }
- }
+ // 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 be45f18..7ad46a1 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -975,7 +975,7 @@
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 @@
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 eb726c1..57b3223 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -20,7 +20,6 @@
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 @@
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 9284574..c55eed9 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 f83dc40..ae44c5e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1823,6 +1823,7 @@
* @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 5bedd7a..9e39355 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 @@
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 4c45c5e..8c2621d 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.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 @@
*/
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 @@
}
@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 3d93964..4612861 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 @@
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 404299a..af8cd1e 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 cf54083..ae6165b 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 @@
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 50fab4f..e09d420 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 @@
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 b3c4374..989010e 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 @@
* 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 4b662137..caa2166 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 b08e513..f1cda27 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 e294dad..b7e86a3 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 47cab42..1086a13 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 7a1371d..22fcd3b 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 09d4685..fb39d3e 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 beac057..51718d9 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 9fe885e..ee24969 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 220ed5c..a9bc9e5 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 215698d..9df9db6 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 6393147..f4086ed 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 01d0dde..db3fae6 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 f98a959..b2ae2a0 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -40,7 +40,8 @@
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 2cf3ad2..c468e41 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 @@
* 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 @@
// 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 0000000..511df4c
--- /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 0000000..2b3e961
--- /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 0000000..2d01d6a
--- /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 0000000..2d51c4d
--- /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 aa7f9a2..28534d3 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.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.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 @@
// 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 @@
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
- LockscreenSmartspaceController smartspaceController) {
+ LockscreenSmartspaceController smartspaceController,
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ SmartspaceTransitionController smartspaceTransitionController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -116,6 +125,9 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mSmartspaceController = smartspaceController;
+
+ mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+ mSmartspaceTransitionController = smartspaceTransitionController;
}
/**
@@ -187,6 +199,7 @@
nic.setLayoutParams(lp);
mView.setSmartspaceView(mSmartspaceView);
+ mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
}
}
@@ -285,12 +298,44 @@
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 299eceb..96eda3d 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.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 java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Set;
/**
* View consisting of:
@@ -55,6 +57,7 @@
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
+ private ViewGroup mStatusViewContainer;
private TextView mLogoutView;
private KeyguardClockSwitch mClockView;
private TextView mOwnerInfo;
@@ -66,6 +69,7 @@
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 @@
@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 @@
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 388c085..7b6514a 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.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 @@
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 @@
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 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 cc167b9..e6fe060 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -133,6 +133,7 @@
.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 1396099..f422e9e 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.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 @@
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 411c328..665376a 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.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 @@
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 @@
/** 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 @@
}
/**
+ * 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 @@
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 8532a46..27a4e93 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 @@
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 @@
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 ffcec29..c74f2fe 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 @@
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 @@
}
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 @@
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 @@
}
}
- 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 @@
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 b0be576..7fe408f 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.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 @@
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 @@
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 @@
}
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 @@
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 @@
*/
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 @@
override fun onError() {
Log.e(TAG, "Cannot resume with $componentName")
mediaDataManager.setResumeAction(key, null)
- mediaBrowser?.disconnect()
mediaBrowser = null
}
@@ -224,7 +210,6 @@
Log.d(TAG, "Can get resumable media from $componentName")
mediaDataManager.setResumeAction(key, getResumeAction(componentName))
updateResumptionList(componentName)
- mediaBrowser?.disconnect()
mediaBrowser = null
}
},
@@ -262,30 +247,7 @@
*/
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 e453653..fecc903 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 @@
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 @@
* @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 @@
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 @@
@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 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 @@
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 @@
@Override
public void onConnectionSuspended() {
Log.d(TAG, "Connection suspended for " + mComponentName);
- mCallback.onError();
+ if (mCallback != null) {
+ mCallback.onError();
+ }
disconnect();
}
@@ -163,16 +183,18 @@
@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 @@
* 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 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 @@
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 9e77b60..d8b342a 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_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 @@
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 @@
};
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 @@
mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
}
- public View getView() {
+ public NavigationBarView getView() {
return mNavigationBarView;
}
@@ -504,16 +506,19 @@
| 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 @@
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 @@
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 @@
@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 @@
mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
+
+ mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -657,10 +662,8 @@
@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 @@
mOrientationHandleGlobalLayoutListener);
}
mHandler.removeCallbacks(mAutoDim);
+ mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
+ mHandler.removeCallbacks(mEnableLayoutTransitions);
mNavigationBarView = null;
mOrientationHandle = null;
}
@@ -682,9 +687,7 @@
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 @@
// 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 @@
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 @@
if (hints == mNavigationIconHints) return;
mNavigationIconHints = hints;
-
- if (mNavigationBarView != null) {
- mNavigationBarView.setNavigationIconHints(hints);
- }
+ mNavigationBarView.setNavigationIconHints(hints);
checkBarModes();
updateSystemUiStateFlags(-1);
}
@@ -911,23 +903,12 @@
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 @@
+ ", 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 @@
boolean nbModeChanged = false;
if (mAppearance != appearance) {
mAppearance = appearance;
- if (getView() == null) {
- return;
- }
nbModeChanged = updateBarMode(barMode(mTransientShown, appearance));
}
if (mLightBarController != null) {
@@ -994,9 +971,7 @@
}
if (mBehavior != behavior) {
mBehavior = behavior;
- if (mNavigationBarView != null) {
- mNavigationBarView.setBehavior(behavior);
- }
+ mNavigationBarView.setBehavior(behavior);
updateSystemUiStateFlags(-1);
}
}
@@ -1034,12 +1009,7 @@
}
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 @@
| StatusBarManager.DISABLE_SEARCH);
if (masked != mDisabledFlags1) {
mDisabledFlags1 = masked;
- if (mNavigationBarView != null) {
- mNavigationBarView.setDisabledFlags(state1);
- }
+ mNavigationBarView.setDisabledFlags(state1);
updateScreenPinningGestures();
}
@@ -1110,17 +1078,13 @@
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 @@
}
private void repositionNavigationBar() {
- if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
+ if (!mNavigationBarView.isAttachedToWindow()) return;
prepareNavigationBarView();
@@ -1138,10 +1102,6 @@
}
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 @@
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 @@
}
void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
- if (mNavigationBarView == null) {
- return;
- }
boolean[] feedbackEnabled = new boolean[1];
int a11yFlags = getA11yButtonState(feedbackEnabled);
@@ -1593,7 +1547,7 @@
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 NavigationBarTransitions getBarTransitions() {
- if (mNavigationBarView != null) {
- return mNavigationBarView.getBarTransitions();
- }
- return null;
+ return mNavigationBarView.getBarTransitions();
}
public void finishBarAnimations() {
@@ -1626,8 +1577,7 @@
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
- if (mNavigationBarView != null
- && mNavigationBarView.needsReorient(rotation)) {
+ if (mNavigationBarView.needsReorient(rotation)) {
repositionNavigationBar();
}
};
@@ -1635,9 +1585,6 @@
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 ae9b598..8b5a537 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 @@
// 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 @@
if (navBar == null) {
continue;
}
- NavigationBarView view = (NavigationBarView) navBar.getView();
- if (view != null) {
- view.updateStates();
- }
+ navBar.getView().updateStates();
}
});
}
@@ -366,13 +362,12 @@
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 @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 4d25079..0218016 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 @@
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 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 be7adbc..74d3425 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 @@
} 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 @@
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 e467925..64aec5e 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 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 89cb5af..f4c15fb 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_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.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 @@
private final CommandQueue mCommandQueue;
private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
+ private final SmartspaceTransitionController mSmartspaceTransitionController;
private Region mActiveNavBarRegion;
@@ -552,6 +555,9 @@
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 @@
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 @@
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 0000000..89b3df0
--- /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 ac6d9e8..c7b6e67 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 @@
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 b33424c..acfd998 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.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 @@
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 @@
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 @@
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 b6357b8..045a197 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 @@
*/
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 889dfde..9dc4ac9 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 @@
&& 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 655ed41..33aa7c7 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.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 @@
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 @@
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 @@
init {
coordinator.attachScheduler(this)
+ dumpManager.registerDumpable(TAG, this)
}
fun onStatusEvent(event: StatusEvent) {
@@ -293,6 +295,20 @@
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 d9cbfda..86465b6 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 @@
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 @@
}
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 7f919b5..4d8e7de3 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_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.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.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.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.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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
@Override
public void onKeyguardShowingChanged() {
if (mKeyguardStateController.isShowing()) {
- queryWalletCards();
+ if (mQuickAccessWalletController != null) {
+ mQuickAccessWalletController.queryWalletCards(mCardRetriever);
+ }
}
}
@@ -935,50 +932,17 @@
/**
* 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 @@
.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 0c4bec2c..eef2420 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 @@
// 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 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 7407143..7737420 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.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.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.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 @@
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 @@
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 @@
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 @@
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
mPrivacyDotViewController = privacyDotViewController;
+ mQuickAccessWalletController = quickAccessWalletController;
mMetricsLogger = metricsLogger;
mActivityManager = activityManager;
mConfigurationController = configurationController;
@@ -721,7 +722,6 @@
mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
mUserManager = userManager;
mMediaDataManager = mediaDataManager;
- mQuickAccessWalletClient = quickAccessWalletClient;
mTapAgainViewController = tapAgainViewController;
mUiExecutor = uiExecutor;
mSecureSettings = secureSettings;
@@ -1122,7 +1122,7 @@
mKeyguardBottomArea.setFalsingManager(mFalsingManager);
if (mFeatureFlags.isQuickAccessWalletEnabled()) {
- mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor, mSecureSettings);
+ mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
}
}
@@ -1259,7 +1259,7 @@
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayCutoutTopInset,
- shouldUseSplitNotificationShade(mFeatureFlags, mResources));
+ mShouldUseSplitNotificationShade);
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = animate || mAnimateNextPositionUpdate;
@@ -1443,7 +1443,7 @@
}
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 @@
}
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 @@
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 @@
@Override
public void onAnimationEnd(Animator animation) {
+ mQSAnimatingHiddenFromCollapsed = false;
mAnimatingQS = false;
notifyExpandingFinished();
mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
@@ -2543,6 +2554,7 @@
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 ae018ba..52f9aca 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 @@
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 ba2340e..c27497e 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 @@
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 @@
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mFeatureFlags = featureFlags;
+ mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
lockscreenShadeTransitionController.setStatusbar(this);
@@ -1369,7 +1372,8 @@
// 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 @@
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
+ updateLightRevealScrimVisibility();
Trace.endSection();
}
@@ -4818,6 +4823,11 @@
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 8befe80..edcf261 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.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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
* @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 b10e841..d3953df 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 @@
&& !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 @@
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 9856795..52e0e8a 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.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 @@
/**
* 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 bb53bad..0c5502b 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 @@
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 e7201f8..af7bf95 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 @@
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 e69c1f2..0945a3f 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.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 @@
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
+ private final SmartspaceTransitionController mSmartspaceTransitionController;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -96,10 +98,12 @@
*/
@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 @@
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 @@
}
@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 07e9fed..4ab07af 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 @@
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 @@
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 4284148..c5e35a4 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.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 @@
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 @@
@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 @@
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);
- Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
- if (icon == null) {
- toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
- } else {
- ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon))
- .setImageDrawable(icon);
- }
- } else {
- toastView = ToastPresenter.getTextToastView(mContext, 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) {
+ iconView.setVisibility(View.GONE);
+ } else {
+ 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.");
+ }
+ }
+ }
+ }
return toastView;
}
@@ -205,18 +228,14 @@
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 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 8b782d4..148bffa 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.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 @@
// 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 @@
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 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 92ea1d0..42f6687 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 @@
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.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 @@
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 @@
/**
* 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 @@
}
@Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
public void onAnimationEnd(Animator animation) {
mPrevPresenter.hide(mPrevCallback);
if (mShowNextToastRunnable != null) {
@@ -221,15 +219,5 @@
}
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 7244ffe..26f4a2b 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 @@
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 f3a95f7..bf00667 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.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.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 @@
/**
* 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 @@
}
/**
- * 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 b9b7730..0b2f004 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 @@
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 50b8858..961822a 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 @@
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);
+ unionViewBoundstoTouchableRegion(mDialogView.getChildAt(i));
+ }
- 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);
+ 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 @@
}
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 0000000..9d0cc6a
--- /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 83aa01f..c6123e7 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.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 @@
*/
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 @@
@Inject
public WalletActivity(
- QuickAccessWalletClient quickAccessWalletClient,
KeyguardStateController keyguardStateController,
KeyguardDismissUtil keyguardDismissUtil,
ActivityStarter activityStarter,
@@ -74,7 +74,6 @@
FalsingManager falsingManager,
UserTracker userTracker,
StatusBarKeyguardViewManager keyguardViewManager) {
- mQuickAccessWalletClient = quickAccessWalletClient;
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
mActivityStarter = activityStarter;
@@ -103,10 +102,11 @@
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 @@
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 @@
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 10322f2..06b0bb2 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.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 @@
@Mock
Resources mResources;
+ KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock
+ SmartspaceTransitionController mSmartSpaceTransitionController;
@Mock
private ClockPlugin mClockPlugin;
@Mock
@@ -135,7 +140,10 @@
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 49f1655..f9b6d44 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.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 @@
ConfigurationController mConfigurationController;
@Mock
DozeParameters mDozeParameters;
+ @Mock
+ KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock
+ SmartspaceTransitionController mSmartSpaceTransitionController;
private KeyguardStatusViewController mController;
@@ -64,7 +70,9 @@
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 d6b82f3..2d19f7d 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.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 @@
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 59527f6..dfb149d 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.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 @@
@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 @@
fun tearDown() {
session.release()
mediaDataManager.destroy()
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, originalSmartspaceSetting)
}
@Test
@@ -257,55 +265,6 @@
}
@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 @@
}
@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 96d1d94..4e1627f 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 @@
@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 @@
// 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 @@
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 d26229e..dfa7c66 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 @@
setupBrowserFailed()
resumeBrowser.testConnection()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -111,8 +112,9 @@
setupBrowserConnectionNoResults()
resumeBrowser.testConnection()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -132,8 +134,9 @@
setupBrowserFailed()
resumeBrowser.findRecentMedia()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -143,8 +146,9 @@
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 @@
setupBrowserConnectionNoResults()
resumeBrowser.findRecentMedia()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -173,8 +178,9 @@
setupBrowserConnectionNotPlayable()
resumeBrowser.findRecentMedia()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -193,8 +199,9 @@
setupBrowserFailed()
resumeBrowser.restart()
- // Then it calls onError
+ // Then it calls onError and disconnects
verify(callback).onError()
+ verify(browser).disconnect()
}
@Test
@@ -202,13 +209,11 @@
// 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 7533cf1..b09afab 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.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 @@
@Mock
private SecureSettings mSecureSettings;
@Mock
+ private QuickAccessWalletController mController;
+ @Mock
private FeatureFlags mFeatureFlags;
@Captor
ArgumentCaptor<Intent> mIntentCaptor;
@@ -145,6 +148,8 @@
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 @@
mStatusBarStateController,
mActivityStarter,
mQSLogger,
- mQuickAccessWalletClient,
mKeyguardStateController,
mPackageManager,
mSecureSettings,
MoreExecutors.directExecutor(),
+ mController,
mFeatureFlags);
}
@@ -175,6 +180,15 @@
}
@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 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 @@
}
@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 @@
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 @@
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 9b5c33d..116f807 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 @@
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 83a1872..ee8d120 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.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.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 @@
@Mock
private LockIconViewController mLockIconViewController;
@Mock
- private QuickAccessWalletClient mQuickAccessWalletClient;
- @Mock
private KeyguardMediaController mKeyguardMediaController;
@Mock
private PrivacyDotViewController mPrivacyDotViewController;
@@ -267,6 +265,8 @@
private FragmentService mFragmentService;
@Mock
private FragmentHostManager mFragmentHostManager;
+ @Mock
+ private QuickAccessWalletController mQuickAccessWalletController;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -380,11 +380,11 @@
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 0000000..4796cd7
--- /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 53a2efc..4b87ec8 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 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 @@
@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 365c62c..9b177e1 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.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.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 @@
@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 @@
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 @@
@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 @@
@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 @@
}
@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 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 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 @@
}
@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 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 5fb779a..1aebf1c 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 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 0000000..33666bc
--- /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 0408309..71d6a48 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 @@
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 @@
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 @@
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 b44fe9f..3e6a0a8 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 @@
* 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 @@
* 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 @@
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 @@
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 6cb374a..4e6e91a 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 @@
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 847e82f..c286556 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 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 @@
*
* <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 @@
/**
* 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 @@
}
}
}
- 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 @@
/**
* 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 @@
/**
* 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 0ffaccf..457fe0f 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 @@
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 d83e13c..1fb5572 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 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 @@
@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 @@
}
@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 fd59e85..dbb2f65 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 @@
@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 @@
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 @@
} 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 @@
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 @@
}
}
- 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 3f2b8ff..b714c6d 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.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 @@
ERROR_UNLOCK_ALL_USERS,
ERROR_PROVIDER_MISMATCH,
ERROR_KEYSTORE_FAILURE,
+ ERROR_NO_NETWORK,
})
@Retention(RetentionPolicy.SOURCE)
@interface RebootEscrowErrorCode {
@@ -139,6 +143,7 @@
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 @@
"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 @@
}
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 @@
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 f0e6939..0dd9b29 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 @@
}
}
+ /** 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 @@
// 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 946f8d5..83d4ce7 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__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 @@
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 @@
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 983b5b1..9c25159 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 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 @@
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 @@
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 @@
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 @@
} 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 @@
// 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 @@
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 a82c91e..dc868b3 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.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 @@
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 @@
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 @@
registerBatteryVoltage();
registerBatteryCycleCount();
registerSettingsStats();
+ registerInstalledIncrementalPackages();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -3949,6 +3956,31 @@
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 0bc3d4a..38966b9 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.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 @@
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 8262c59..66e55e0 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 @@
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 57c2a13..134ecde 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 @@
@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 @@
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 b140451..7fe0f5b 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 @@
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 13fd76c..306137a 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 @@
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 2330482..039422d 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.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 @@
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 @@
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 2d2b1f4..2ee5fb0 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 @@
}
}
- /**
- * 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 68cb8f9..3dc7bc1 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 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.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 @@
@Mock
private Context mContext;
@Mock
+ private BatteryManagerInternal mBatteryManagerInternal;
+ @Mock
private ConnectivityManager mConnManager;
@Mock
private NetworkPolicyManager mNetPolicyManager;
@@ -108,6 +115,9 @@
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 @@
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 @@
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 @@
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 f85f0f8..50d4d84 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 org.junit.Test;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
/**
* Build/Install/Run:
* atest FrameworksServicesTests:TempAllowListTest
@@ -41,7 +44,7 @@
*/
@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 @@
assertNotNull(entry2);
assertEquals(entry2.second, "description2");
- allowList.remove(10001);
+ allowList.removeUid(10001);
assertFalse(allowList.isAllowed(10001));
assertNull(allowList.get(10001));
}
@@ -65,7 +68,7 @@
*/
@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 @@
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 aecc794..b01c1c8 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 @@
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 @@
}
@Override
+ public boolean isNetworkConnected() {
+ return false;
+ }
+
+ @Override
public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
return mRebootEscrowProviderInUse;
@@ -602,7 +617,7 @@
// 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 ef5f00b..fdb6e9f5 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.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 @@
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 @@
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 c527e66..6b7fc2f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3682,6 +3682,13 @@
"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 @@
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 2b4fb7d..734b520 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -79,7 +79,7 @@
* 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 08513c2..fe14dd1 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -34,7 +34,7 @@
/**
* @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 c078637..42c53f2 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -136,7 +136,7 @@
* 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 @@
*
* @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 91ecbf0..a096c1f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2440,6 +2440,12 @@
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 0000000..297d9c3b
--- /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 2caf9ed..8381522 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