diff options
166 files changed, 4236 insertions, 1573 deletions
diff --git a/Android.bp b/Android.bp index 4d8924d28859..6c5acd2b416b 100644 --- a/Android.bp +++ b/Android.bp @@ -273,9 +273,12 @@ java_library { installable: false, } +// NOTE: This filegroup is exposed for vendor libraries to depend on and is referenced in +// documentation. Do not remove without consulting the treble/hidl teams. filegroup { name: "framework-jarjar-rules", srcs: ["framework-jarjar-rules.txt"], + visibility: ["//visibility:public"], } java_defaults { diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index b3c33b6615f4..144536ec2644 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1229,7 +1229,11 @@ public class JobInfo implements Parcelable { * </ul> * Note that the system may choose to delay jobs with large network * usage estimates when the device has a poor network connection, in - * order to save battery. + * order to save battery and possible network costs. + * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt + * to run large jobs when the device is charging and on an unmetered network, even if the + * network is slow. This gives large jobs an opportunity to make forward progress, even if + * they risk timing out. * <p> * The values provided here only reflect the traffic that will be * performed by the base job; if you're using {@link JobWorkItem} then diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 60f5769a46f7..cef065ddac9e 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -547,7 +547,7 @@ public class DeviceIdleController extends SystemService private int[] mPowerSaveWhitelistUserAppIdArray = new int[0]; /** - * List of end times for UIDs that are temporarily marked as being allowed to access + * List of end times for app-IDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 5365218203d9..1169391d2cd2 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -121,7 +121,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; -import com.android.internal.os.BinderDeathDispatcher; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LocalLog; @@ -184,8 +183,7 @@ public class AlarmManagerService extends SystemService { static final boolean DEBUG_BG_LIMIT = localLOGV || false; static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; - // TODO(b/178484639) : Turn off once alarms and reminders work is complete. - static final boolean RECORD_DEVICE_IDLE_ALARMS = true; + static final boolean RECORD_DEVICE_IDLE_ALARMS = false; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; static final int TICK_HISTORY_DEPTH = 10; @@ -206,8 +204,6 @@ public class AlarmManagerService extends SystemService { .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher = - new BinderDeathDispatcher<>(); final LocalLog mLog = new LocalLog(TAG); AppOpsManager mAppOps; @@ -1837,8 +1833,9 @@ public class AlarmManagerService extends SystemService { } if (directReceiver != null) { - if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient) - <= 0) { + try { + directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0); + } catch (RemoteException e) { Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag); return; } @@ -2851,12 +2848,6 @@ public class AlarmManagerService extends SystemService { pw.println(); } - pw.println("Listener death dispatcher state:"); - pw.increaseIndent(); - sListenerDeathDispatcher.dump(pw); - pw.println(); - pw.decreaseIndent(); - if (mLog.dump(pw, "Recent problems:")) { pw.println(); } @@ -3448,6 +3439,9 @@ public class AlarmManagerService extends SystemService { for (final Alarm removed : removedAlarms) { decrementAlarmCount(removed.uid, 1); + if (removed.listener != null) { + removed.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0); + } if (!RemovedAlarm.isLoggable(reason)) { continue; } @@ -4701,6 +4695,8 @@ public class AlarmManagerService extends SystemService { // Direct listener callback alarm mListenerCount++; + alarm.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0); + if (RECORD_ALARMS_IN_HISTORY) { if (alarm.listener == mTimeTickTrigger) { mTickHistory[mNextTickHistory++] = nowELAPSED; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 7b947fd3ab43..e8065aa535f3 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -25,12 +25,18 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -101,6 +107,8 @@ public final class ConnectivityController extends RestrictingController implemen private final ConnectivityManager mConnManager; private final NetworkPolicyManagerInternal mNetPolicyManagerInternal; + private final ChargingTracker mChargingTracker; + /** List of tracked jobs keyed by source UID. */ @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>(); @@ -229,6 +237,9 @@ public final class ConnectivityController extends RestrictingController implemen // network changes against the active network for each UID with jobs. final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mConnManager.registerNetworkCallback(request, mNetworkCallback); + + mChargingTracker = new ChargingTracker(); + mChargingTracker.startTracking(); } @GuardedBy("mLock") @@ -538,6 +549,14 @@ public final class ConnectivityController extends RestrictingController implemen */ private boolean isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { + if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED) + && mChargingTracker.isCharging()) { + // We're charging and on an unmetered network. We don't have to be as conservative about + // making sure the job will run within its max execution time. Let's just hope the app + // supports interruptible work. + return false; + } + // Use the maximum possible time since it gives us an upper bound, even though the job // could end up stopping earlier. final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus); @@ -922,7 +941,7 @@ public final class ConnectivityController extends RestrictingController implemen * {@link Network}, or {@code null} to update all tracked jobs. */ @GuardedBy("mLock") - private void updateTrackedJobsLocked(int filterUid, Network filterNetwork) { + private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) { boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { @@ -937,7 +956,8 @@ public final class ConnectivityController extends RestrictingController implemen } @GuardedBy("mLock") - private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) { + private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, + @Nullable Network filterNetwork) { if (jobs == null || jobs.size() == 0) { return false; } @@ -993,6 +1013,51 @@ public final class ConnectivityController extends RestrictingController implemen } } + private final class ChargingTracker extends BroadcastReceiver { + /** + * Track whether we're "charging", where charging means that we're ready to commit to + * doing work. + */ + private boolean mCharging; + + ChargingTracker() {} + + public void startTracking() { + IntentFilter filter = new IntentFilter(); + filter.addAction(BatteryManager.ACTION_CHARGING); + filter.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(this, filter); + + // Initialise tracker state. + final BatteryManagerInternal batteryManagerInternal = + LocalServices.getService(BatteryManagerInternal.class); + mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); + } + + public boolean isCharging() { + return mCharging; + } + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + final String action = intent.getAction(); + if (BatteryManager.ACTION_CHARGING.equals(action)) { + if (mCharging) { + return; + } + mCharging = true; + } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { + if (!mCharging) { + return; + } + mCharging = false; + } + updateTrackedJobsLocked(-1, null); + } + } + } + private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onAvailable(Network network) { diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp index 9b3399e8b0e1..271fc5312f8f 100644 --- a/apex/media/service/Android.bp +++ b/apex/media/service/Android.bp @@ -23,10 +23,10 @@ package { filegroup { name: "service-media-s-sources", srcs: [ - "java/**/*.java", + "java/**/*.java", ], path: "java", - visibility: ["//frameworks/base/services"], // TODO(b/177640454): Should be private. + visibility: ["//visibility:private"], } java_sdk_library { diff --git a/boot/Android.bp b/boot/Android.bp index 3caede47d859..e8d88a531b5b 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -48,13 +48,57 @@ platform_bootclasspath { // bootclasspath. fragments: [ { + apex: "com.android.appsearch", + module: "com.android.appsearch-bootclasspath-fragment", + }, + { apex: "com.android.art", module: "art-bootclasspath-fragment", }, { + apex: "com.android.conscrypt", + module: "com.android.conscrypt-bootclasspath-fragment", + }, + { apex: "com.android.i18n", module: "i18n-bootclasspath-fragment", }, + { + apex: "com.android.ipsec", + module: "com.android.ipsec-bootclasspath-fragment", + }, + { + apex: "com.android.media", + module: "com.android.media-bootclasspath-fragment", + }, + { + apex: "com.android.mediaprovider", + module: "com.android.mediaprovider-bootclasspath-fragment", + }, + { + apex: "com.android.os.statsd", + module: "com.android.os.statsd-bootclasspath-fragment", + }, + { + apex: "com.android.permission", + module: "com.android.permission-bootclasspath-fragment", + }, + { + apex: "com.android.scheduling", + module: "com.android.scheduling-bootclasspath-fragment", + }, + { + apex: "com.android.sdkext", + module: "com.android.sdkext-bootclasspath-fragment", + }, + { + apex: "com.android.tethering", + module: "com.android.tethering-bootclasspath-fragment", + }, + { + apex: "com.android.wifi", + module: "com.android.wifi-bootclasspath-fragment", + }, ], // Additional information needed by hidden api processing. diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 45ebbb1ea526..3cc28d908a38 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -93863,283 +93863,6 @@ Lcom/android/internal/widget/VerifyCredentialResponse;->setPayload([B)V Lcom/android/internal/widget/VerifyCredentialResponse;->setTimeout(I)V Lcom/android/internal/widget/VerifyCredentialResponse;->stripPayload()Lcom/android/internal/widget/VerifyCredentialResponse; Lcom/android/internal/widget/VerifyCredentialResponse;->TAG:Ljava/lang/String; -Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>()V -Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/lang/String;I)V -Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/net/InetAddress;I)V -Lcom/android/org/conscrypt/AbstractConscryptSocket;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/AbstractConscryptSocket;->getFileDescriptor$()Ljava/io/FileDescriptor; -Lcom/android/org/conscrypt/AbstractConscryptSocket;->getTlsUnique()[B -Lcom/android/org/conscrypt/AbstractConscryptSocket;->peerInfoProvider()Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/AbstractConscryptSocket;->setApplicationProtocolSelector(Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V -Lcom/android/org/conscrypt/ApplicationProtocolSelector;-><init>()V -Lcom/android/org/conscrypt/ApplicationProtocolSelector;->selectApplicationProtocol(Ljavax/net/ssl/SSLEngine;Ljava/util/List;)Ljava/lang/String; -Lcom/android/org/conscrypt/ApplicationProtocolSelector;->selectApplicationProtocol(Ljavax/net/ssl/SSLSocket;Ljava/util/List;)Ljava/lang/String; -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;-><init>(Ljavax/net/ssl/SSLEngine;Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;-><init>(Ljavax/net/ssl/SSLSocket;Lcom/android/org/conscrypt/ApplicationProtocolSelector;)V -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->engine:Ljavax/net/ssl/SSLEngine; -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->NO_PROTOCOL_SELECTED:I -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->selectApplicationProtocol([B)I -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->selector:Lcom/android/org/conscrypt/ApplicationProtocolSelector; -Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;->socket:Ljavax/net/ssl/SSLSocket; -Lcom/android/org/conscrypt/CertBlacklist;-><init>(Ljava/util/Set;Ljava/util/Set;)V -Lcom/android/org/conscrypt/CertBlacklist;->closeQuietly(Ljava/io/Closeable;)V -Lcom/android/org/conscrypt/CertBlacklist;->getDefault()Lcom/android/org/conscrypt/CertBlacklist; -Lcom/android/org/conscrypt/CertBlacklist;->HEX_TABLE:[B -Lcom/android/org/conscrypt/CertBlacklist;->isHex(Ljava/lang/String;)Z -Lcom/android/org/conscrypt/CertBlacklist;->isPubkeyHash(Ljava/lang/String;)Z -Lcom/android/org/conscrypt/CertBlacklist;->isPublicKeyBlackListed(Ljava/security/PublicKey;)Z -Lcom/android/org/conscrypt/CertBlacklist;->isSerialNumberBlackListed(Ljava/math/BigInteger;)Z -Lcom/android/org/conscrypt/CertBlacklist;->logger:Ljava/util/logging/Logger; -Lcom/android/org/conscrypt/CertBlacklist;->pubkeyBlacklist:Ljava/util/Set; -Lcom/android/org/conscrypt/CertBlacklist;->readBlacklist(Ljava/lang/String;)Ljava/lang/String; -Lcom/android/org/conscrypt/CertBlacklist;->readFileAsBytes(Ljava/lang/String;)Ljava/io/ByteArrayOutputStream; -Lcom/android/org/conscrypt/CertBlacklist;->readFileAsString(Ljava/lang/String;)Ljava/lang/String; -Lcom/android/org/conscrypt/CertBlacklist;->readPublicKeyBlackList(Ljava/lang/String;)Ljava/util/Set; -Lcom/android/org/conscrypt/CertBlacklist;->readSerialBlackList(Ljava/lang/String;)Ljava/util/Set; -Lcom/android/org/conscrypt/CertBlacklist;->serialBlacklist:Ljava/util/Set; -Lcom/android/org/conscrypt/CertBlacklist;->toHex([B)[B -Lcom/android/org/conscrypt/CertificatePriorityComparator;-><init>()V -Lcom/android/org/conscrypt/CertificatePriorityComparator;->ALGORITHM_OID_PRIORITY_MAP:Ljava/util/Map; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compare(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareKeyAlgorithm(Ljava/security/PublicKey;Ljava/security/PublicKey;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareKeySize(Ljava/security/PublicKey;Ljava/security/PublicKey;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareSignatureAlgorithm(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->compareStrength(Ljava/security/cert/X509Certificate;Ljava/security/cert/X509Certificate;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->getKeySize(Ljava/security/PublicKey;)I -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_MD5:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA1:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA224:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA256:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA384:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_SHA512:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertificatePriorityComparator;->PRIORITY_UNKNOWN:Ljava/lang/Integer; -Lcom/android/org/conscrypt/CertPinManager;->checkChainPinning(Ljava/lang/String;Ljava/util/List;)V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>()V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/lang/String;I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/InetAddress;I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/ConscryptSocketBase;-><init>(Ljava/net/Socket;Ljava/lang/String;IZ)V -Lcom/android/org/conscrypt/ConscryptSocketBase;->autoClose:Z -Lcom/android/org/conscrypt/ConscryptSocketBase;->checkOpen()V -Lcom/android/org/conscrypt/ConscryptSocketBase;->getActiveSession()Ljavax/net/ssl/SSLSession; -Lcom/android/org/conscrypt/ConscryptSocketBase;->getFileDescriptor$()Ljava/io/FileDescriptor; -Lcom/android/org/conscrypt/ConscryptSocketBase;->isDelegating()Z -Lcom/android/org/conscrypt/ConscryptSocketBase;->listeners:Ljava/util/List; -Lcom/android/org/conscrypt/ConscryptSocketBase;->notifyHandshakeCompletedListeners()V -Lcom/android/org/conscrypt/ConscryptSocketBase;->peerHostname:Ljava/lang/String; -Lcom/android/org/conscrypt/ConscryptSocketBase;->peerInfoProvider()Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/ConscryptSocketBase;->peerInfoProvider:Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/ConscryptSocketBase;->peerPort:I -Lcom/android/org/conscrypt/ConscryptSocketBase;->readTimeoutMilliseconds:I -Lcom/android/org/conscrypt/ConscryptSocketBase;->setApplicationProtocolSelector(Lcom/android/org/conscrypt/ApplicationProtocolSelectorAdapter;)V -Lcom/android/org/conscrypt/NativeRef$EC_GROUP;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EC_GROUP;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$EC_POINT;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EC_POINT;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_CIPHER_CTX;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_CIPHER_CTX;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_MD_CTX;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_MD_CTX;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_PKEY_CTX;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$EVP_PKEY_CTX;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$HMAC_CTX;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$HMAC_CTX;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef$SSL_SESSION;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef$SSL_SESSION;->doFree(J)V -Lcom/android/org/conscrypt/NativeRef;-><init>(J)V -Lcom/android/org/conscrypt/NativeRef;->context:J -Lcom/android/org/conscrypt/NativeRef;->doFree(J)V -Lcom/android/org/conscrypt/OpenSSLKey;-><init>(JZ)V -Lcom/android/org/conscrypt/OpenSSLKey;->ctx:Lcom/android/org/conscrypt/NativeRef$EVP_PKEY; -Lcom/android/org/conscrypt/OpenSSLKey;->fromECPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/spec/ECParameterSpec;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->fromKeyMaterial(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKeyPemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->fromPublicKey(Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->fromPublicKeyPemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->getOpenSSLKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->getPrivateKey()Ljava/security/PrivateKey; -Lcom/android/org/conscrypt/OpenSSLKey;->getPrivateKey(Ljava/security/spec/PKCS8EncodedKeySpec;I)Ljava/security/PrivateKey; -Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey(Ljava/security/spec/X509EncodedKeySpec;I)Ljava/security/PublicKey; -Lcom/android/org/conscrypt/OpenSSLKey;->isWrapped()Z -Lcom/android/org/conscrypt/OpenSSLKey;->wrapJCAPrivateKeyForTLSStackOnly(Ljava/security/PrivateKey;Ljava/security/PublicKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLKey;->wrapped:Z -Lcom/android/org/conscrypt/OpenSSLKey;->wrapPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey; -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>()V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/lang/String;I)V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/lang/String;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/InetAddress;I)V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I)V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;-><init>(Ljava/net/Socket;Ljava/lang/String;IZ)V -Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getFileDescriptor$()Ljava/io/FileDescriptor; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;-><init>(J)V -Lcom/android/org/conscrypt/OpenSSLX509Certificate;-><init>(JLjava/util/Date;Ljava/util/Date;)V -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->alternativeNameArrayToList([[Ljava/lang/Object;)Ljava/util/Collection; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromCertificate(Ljava/security/cert/Certificate;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509Der([B)Lcom/android/org/conscrypt/OpenSSLX509Certificate; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509DerInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->getContext()J -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mHashCode:Ljava/lang/Integer; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->notAfter:Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->notBefore:Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->toDate(J)Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->verifyInternal(Ljava/security/PublicKey;Ljava/lang/String;)V -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->verifyOpenSSL(Lcom/android/org/conscrypt/OpenSSLKey;)V -Lcom/android/org/conscrypt/OpenSSLX509Certificate;->withDeletedExtension(Ljava/lang/String;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;-><init>()V -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromX509DerInputStream(Ljava/io/InputStream;)Ljava/lang/Object; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->fromX509PemInputStream(Ljava/io/InputStream;)Ljava/lang/Object; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->generateItem(Ljava/io/InputStream;)Ljava/lang/Object; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser;->generateItems(Ljava/io/InputStream;)Ljava/util/Collection; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/Exception;)V -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/String;)V -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$ParsingException;-><init>(Ljava/lang/String;Ljava/lang/Exception;)V -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;-><init>()V -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->certificateParser:Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->crlParser:Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory$Parser; -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->PKCS7_MARKER:[B -Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;->PUSHBACK_SIZE:I -Lcom/android/org/conscrypt/OpenSSLX509CRL;-><init>(J)V -Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromPkcs7DerInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromPkcs7PemInputStream(Ljava/io/InputStream;)Ljava/util/List; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromX509DerInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509CRL; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->fromX509PemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509CRL; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->mContext:J -Lcom/android/org/conscrypt/OpenSSLX509CRL;->nextUpdate:Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->thisUpdate:Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->toDate(J)Ljava/util/Date; -Lcom/android/org/conscrypt/OpenSSLX509CRL;->verifyInternal(Ljava/security/PublicKey;Ljava/lang/String;)V -Lcom/android/org/conscrypt/OpenSSLX509CRL;->verifyOpenSSL(Lcom/android/org/conscrypt/OpenSSLKey;)V -Lcom/android/org/conscrypt/PeerInfoProvider;-><init>()V -Lcom/android/org/conscrypt/PeerInfoProvider;->forHostAndPort(Ljava/lang/String;I)Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/PeerInfoProvider;->getHostname()Ljava/lang/String; -Lcom/android/org/conscrypt/PeerInfoProvider;->getHostnameOrIP()Ljava/lang/String; -Lcom/android/org/conscrypt/PeerInfoProvider;->getPort()I -Lcom/android/org/conscrypt/PeerInfoProvider;->nullProvider()Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/PeerInfoProvider;->NULL_PEER_INFO_PROVIDER:Lcom/android/org/conscrypt/PeerInfoProvider; -Lcom/android/org/conscrypt/SSLClientSessionCache;->getSessionData(Ljava/lang/String;I)[B -Lcom/android/org/conscrypt/SSLClientSessionCache;->putSessionData(Ljavax/net/ssl/SSLSession;[B)V -Lcom/android/org/conscrypt/TrustedCertificateIndex;-><init>()V -Lcom/android/org/conscrypt/TrustedCertificateIndex;-><init>(Ljava/util/Set;)V -Lcom/android/org/conscrypt/TrustedCertificateIndex;->findAllByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/util/Set; -Lcom/android/org/conscrypt/TrustedCertificateIndex;->findByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; -Lcom/android/org/conscrypt/TrustedCertificateIndex;->findBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; -Lcom/android/org/conscrypt/TrustedCertificateIndex;->findBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;Ljava/util/Collection;)Ljava/security/cert/TrustAnchor; -Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/security/cert/TrustAnchor;)V -Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; -Lcom/android/org/conscrypt/TrustedCertificateIndex;->index(Ljava/util/Set;)V -Lcom/android/org/conscrypt/TrustedCertificateIndex;->reset()V -Lcom/android/org/conscrypt/TrustedCertificateIndex;->reset(Ljava/util/Set;)V -Lcom/android/org/conscrypt/TrustedCertificateIndex;->subjectToTrustAnchors:Ljava/util/Map; -Lcom/android/org/conscrypt/TrustedCertificateStore$CertSelector;->match(Ljava/security/cert/X509Certificate;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;-><init>()V -Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsAddedDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsDeletedDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore$PreloadHolder;->defaultCaCertsSystemDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;-><init>(Ljava/io/File;Ljava/io/File;Ljava/io/File;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->addAliases(Ljava/util/Set;Ljava/lang/String;Ljava/io/File;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->addedDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->aliases()Ljava/util/Set; -Lcom/android/org/conscrypt/TrustedCertificateStore;->allSystemAliases()Ljava/util/Set; -Lcom/android/org/conscrypt/TrustedCertificateStore;->CERT_FACTORY:Ljava/security/cert/CertificateFactory; -Lcom/android/org/conscrypt/TrustedCertificateStore;->containsAlias(Ljava/lang/String;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->containsAlias(Ljava/lang/String;Z)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->convertToOpenSSLIfNeeded(Ljava/security/cert/X509Certificate;)Lcom/android/org/conscrypt/OpenSSLX509Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->deleteCertificateEntry(Ljava/lang/String;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->deletedDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->file(Ljava/io/File;Ljava/lang/String;I)Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->fileForAlias(Ljava/lang/String;)Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->findAllIssuers(Ljava/security/cert/X509Certificate;)Ljava/util/Set; -Lcom/android/org/conscrypt/TrustedCertificateStore;->findCert(Ljava/io/File;Ljavax/security/auth/x500/X500Principal;Lcom/android/org/conscrypt/TrustedCertificateStore$CertSelector;Ljava/lang/Class;)Ljava/lang/Object; -Lcom/android/org/conscrypt/TrustedCertificateStore;->findIssuer(Ljava/security/cert/X509Certificate;)Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificate(Ljava/lang/String;)Ljava/security/cert/Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificate(Ljava/lang/String;Z)Ljava/security/cert/Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateAlias(Ljava/security/cert/Certificate;)Ljava/lang/String; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateAlias(Ljava/security/cert/Certificate;Z)Ljava/lang/String; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateFile(Ljava/io/File;Ljava/security/cert/X509Certificate;)Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getCreationDate(Ljava/lang/String;)Ljava/util/Date; -Lcom/android/org/conscrypt/TrustedCertificateStore;->getTrustAnchor(Ljava/security/cert/X509Certificate;)Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->hash(Ljavax/security/auth/x500/X500Principal;)Ljava/lang/String; -Lcom/android/org/conscrypt/TrustedCertificateStore;->installCertificate(Ljava/security/cert/X509Certificate;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->isDeletedSystemCertificate(Ljava/security/cert/X509Certificate;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->isSelfIssuedCertificate(Lcom/android/org/conscrypt/OpenSSLX509Certificate;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->isSystem(Ljava/lang/String;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->isTombstone(Ljava/io/File;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->isUser(Ljava/lang/String;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->isUserAddedCertificate(Ljava/security/cert/X509Certificate;)Z -Lcom/android/org/conscrypt/TrustedCertificateStore;->PREFIX_SYSTEM:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustedCertificateStore;->PREFIX_USER:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustedCertificateStore;->readCertificate(Ljava/io/File;)Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustedCertificateStore;->removeUnnecessaryTombstones(Ljava/lang/String;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->setDefaultUserDirectory(Ljava/io/File;)V -Lcom/android/org/conscrypt/TrustedCertificateStore;->systemDir:Ljava/io/File; -Lcom/android/org/conscrypt/TrustedCertificateStore;->userAliases()Ljava/util/Set; -Lcom/android/org/conscrypt/TrustedCertificateStore;->writeCertificate(Ljava/io/File;Ljava/security/cert/X509Certificate;)V -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;-><init>(ZLjava/security/cert/X509Certificate;)V -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->clientAuth:Z -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_anyExtendedKeyUsage:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_clientAuth:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_msSGC:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_nsSGC:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_OID:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->EKU_serverAuth:Ljava/lang/String; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->leaf:Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker;->SUPPORTED_EXTENSIONS:Ljava/util/Set; -Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;-><init>()V -Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->CERT_COMPARATOR:Lcom/android/org/conscrypt/CertificatePriorityComparator; -Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->compare(Ljava/lang/Object;Ljava/lang/Object;)I -Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator;->compare(Ljava/security/cert/TrustAnchor;Ljava/security/cert/TrustAnchor;)I -Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;)V -Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;)V -Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;Lcom/android/org/conscrypt/CertBlacklist;)V -Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;Lcom/android/org/conscrypt/CertPinManager;Lcom/android/org/conscrypt/TrustedCertificateStore;Lcom/android/org/conscrypt/CertBlacklist;Lcom/android/org/conscrypt/ct/CTLogStore;Lcom/android/org/conscrypt/ct/CTVerifier;Lcom/android/org/conscrypt/ct/CTPolicy;)V -Lcom/android/org/conscrypt/TrustManagerImpl;->acceptedIssuers(Ljava/security/KeyStore;)[Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustManagerImpl;->acceptedIssuers:[Ljava/security/cert/X509Certificate; -Lcom/android/org/conscrypt/TrustManagerImpl;->blacklist:Lcom/android/org/conscrypt/CertBlacklist; -Lcom/android/org/conscrypt/TrustManagerImpl;->checkBlacklist(Ljava/security/cert/X509Certificate;)V -Lcom/android/org/conscrypt/TrustManagerImpl;->checkClientTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->checkCT(Ljava/lang/String;Ljava/util/List;[B[B)V -Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLSession;Ljavax/net/ssl/SSLParameters;Z)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrusted([Ljava/security/cert/X509Certificate;[B[BLjava/lang/String;Ljava/lang/String;Z)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->checkTrustedRecursive([Ljava/security/cert/X509Certificate;[B[BLjava/lang/String;ZLjava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/Set;)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->ctEnabledOverride:Z -Lcom/android/org/conscrypt/TrustManagerImpl;->ctPolicy:Lcom/android/org/conscrypt/ct/CTPolicy; -Lcom/android/org/conscrypt/TrustManagerImpl;->ctVerifier:Lcom/android/org/conscrypt/ct/CTVerifier; -Lcom/android/org/conscrypt/TrustManagerImpl;->err:Ljava/lang/Exception; -Lcom/android/org/conscrypt/TrustManagerImpl;->factory:Ljava/security/cert/CertificateFactory; -Lcom/android/org/conscrypt/TrustManagerImpl;->findAllTrustAnchorsByIssuerAndSignature(Ljava/security/cert/X509Certificate;)Ljava/util/Set; -Lcom/android/org/conscrypt/TrustManagerImpl;->findTrustAnchorBySubjectAndPublicKey(Ljava/security/cert/X509Certificate;)Ljava/security/cert/TrustAnchor; -Lcom/android/org/conscrypt/TrustManagerImpl;->getHandshakeSessionOrThrow(Ljavax/net/ssl/SSLSocket;)Ljavax/net/ssl/SSLSession; -Lcom/android/org/conscrypt/TrustManagerImpl;->getOcspDataFromSession(Ljavax/net/ssl/SSLSession;)[B -Lcom/android/org/conscrypt/TrustManagerImpl;->getTlsSctDataFromSession(Ljavax/net/ssl/SSLSession;)[B -Lcom/android/org/conscrypt/TrustManagerImpl;->getTrustedChainForServer([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/net/Socket;)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->getTrustedChainForServer([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljavax/net/ssl/SSLEngine;)Ljava/util/List; -Lcom/android/org/conscrypt/TrustManagerImpl;->handleTrustStorageUpdate()V -Lcom/android/org/conscrypt/TrustManagerImpl;->intermediateIndex:Lcom/android/org/conscrypt/TrustedCertificateIndex; -Lcom/android/org/conscrypt/TrustManagerImpl;->isUserAddedCertificate(Ljava/security/cert/X509Certificate;)Z -Lcom/android/org/conscrypt/TrustManagerImpl;->pinManager:Lcom/android/org/conscrypt/CertPinManager; -Lcom/android/org/conscrypt/TrustManagerImpl;->rootKeyStore:Ljava/security/KeyStore; -Lcom/android/org/conscrypt/TrustManagerImpl;->setCTEnabledOverride(Z)V -Lcom/android/org/conscrypt/TrustManagerImpl;->setCTPolicy(Lcom/android/org/conscrypt/ct/CTPolicy;)V -Lcom/android/org/conscrypt/TrustManagerImpl;->setCTVerifier(Lcom/android/org/conscrypt/ct/CTVerifier;)V -Lcom/android/org/conscrypt/TrustManagerImpl;->setOcspResponses(Ljava/security/cert/PKIXParameters;Ljava/security/cert/X509Certificate;[B)V -Lcom/android/org/conscrypt/TrustManagerImpl;->sortPotentialAnchors(Ljava/util/Set;)Ljava/util/Collection; -Lcom/android/org/conscrypt/TrustManagerImpl;->trustAnchors([Ljava/security/cert/X509Certificate;)Ljava/util/Set; -Lcom/android/org/conscrypt/TrustManagerImpl;->trustedCertificateIndex:Lcom/android/org/conscrypt/TrustedCertificateIndex; -Lcom/android/org/conscrypt/TrustManagerImpl;->trustedCertificateStore:Lcom/android/org/conscrypt/TrustedCertificateStore; -Lcom/android/org/conscrypt/TrustManagerImpl;->TRUST_ANCHOR_COMPARATOR:Lcom/android/org/conscrypt/TrustManagerImpl$TrustAnchorComparator; -Lcom/android/org/conscrypt/TrustManagerImpl;->validator:Ljava/security/cert/CertPathValidator; -Lcom/android/org/conscrypt/TrustManagerImpl;->verifyChain(Ljava/util/List;Ljava/util/List;Ljava/lang/String;Z[B[B)Ljava/util/List; Lorg/apache/http/conn/ssl/AbstractVerifier;->IPV4_PATTERN:Ljava/util/regex/Pattern; Lorg/apache/http/conn/ssl/AbstractVerifier;->isIPv4Address(Ljava/lang/String;)Z Lorg/apache/http/conn/ssl/SSLSocketFactory$NoPreloadHolder;-><init>()V diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f23966e8d164..7072bbeade29 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4930,10 +4930,6 @@ package android.location { field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; } - public final class LocationDeviceConfig { - field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; - } - public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); @@ -4946,7 +4942,6 @@ package android.location { method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle); method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String); - method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); @@ -12843,7 +12838,6 @@ package android.telephony.ims { method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>); method public void onDestroyed(int); method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState); - method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); } public final class FeatureTagState implements android.os.Parcelable { @@ -13664,67 +13658,6 @@ package android.telephony.ims { method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); } - @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { - method @Deprecated public boolean containsKey(@NonNull String); - method @Deprecated @NonNull public android.os.PersistableBundle copyBundle(); - method @Deprecated public int describeContents(); - method @Deprecated public boolean getBoolean(@NonNull String, boolean); - method @Deprecated public int getInt(@NonNull String, int); - method @Deprecated @Nullable public String getString(@NonNull String); - method @Deprecated public long getVersion(); - method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; - field @Deprecated public static final String IPTYPE_IPV4 = "IPV4"; - field @Deprecated public static final String IPTYPE_IPV6 = "IPV6"; - field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; - field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; - field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; - field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; - field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP"; - field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP"; - } - - @Deprecated public static final class SipDelegateImsConfiguration.Builder { - ctor @Deprecated public SipDelegateImsConfiguration.Builder(int); - ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); - method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); - } - public class SipDelegateManager { method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); @@ -13892,11 +13825,10 @@ package android.telephony.ims.stub { } public interface DelegateConnectionStateCallback { - method public default void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration); + method public void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration); method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection); method public void onDestroyed(int); method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>); - method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); } public class ImsCallSessionImplBase implements java.lang.AutoCloseable { diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index 7cf007640b1a..9a8a49397322 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -135,6 +135,7 @@ package android.location { public class LocationManager { method @Deprecated public boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String); method @Deprecated public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackage(String); @@ -231,6 +232,83 @@ package android.telephony.data { } +package android.telephony.ims { + + public interface DelegateStateCallback { + method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + + @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { + method public boolean containsKey(@NonNull String); + method @NonNull public android.os.PersistableBundle copyBundle(); + method public int describeContents(); + method public boolean getBoolean(@NonNull String, boolean); + method public int getInt(@NonNull String, int); + method @Nullable public String getString(@NonNull String); + method public long getVersion(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field public static final String IPTYPE_IPV4 = "IPV4"; + field public static final String IPTYPE_IPV6 = "IPV6"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; + field public static final String SIP_TRANSPORT_TCP = "TCP"; + field public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + public static final class SipDelegateImsConfiguration.Builder { + ctor public SipDelegateImsConfiguration.Builder(int); + ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + } + +} + +package android.telephony.ims.stub { + + public interface DelegateConnectionStateCallback { + method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + +} + package android.view.translation { public final class UiTranslationManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 8499b3739528..2f795f0e103f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1360,10 +1360,6 @@ package android.location { method public void setType(int); } - public final class LocationDeviceConfig { - field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist"; - } - public class LocationManager { method @NonNull public String[] getBackgroundThrottlingWhitelist(); method @NonNull public android.os.PackageTagsList getIgnoreSettingsAllowlist(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index eb0a7b4076ee..8d39d8aa1261 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1182,7 +1182,7 @@ public final class ActivityThread extends ClientTransactionHandler InetAddress.clearDnsCache(); // Allow libcore to perform the necessary actions as it sees fit upon a network // configuration change. - NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged(); + NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange(); } public void updateHttpProxy() { @@ -6413,7 +6413,7 @@ public final class ActivityThread extends ClientTransactionHandler VMDebug.setAllocTrackerStackDepth(Integer.parseInt(property)); } if (data.trackAllocation) { - DdmVmInternal.enableRecentAllocations(true); + DdmVmInternal.setRecentAllocationsTrackingEnabled(true); } // Note when this process has started. Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fc676cf5b9c2..886cd7f48d35 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -400,10 +400,7 @@ public abstract class Context { public static final int BIND_BYPASS_POWER_NETWORK_RESTRICTIONS = 0x00020000; /** - * Flag for {@link #bindService}: allow background foreground service starts from the bound - * service's process. - * This flag is only respected if the caller is holding - * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}. + * Do not use. This flag is no longer needed nor used. * @hide */ @SystemApi diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 33606e855220..33a34be1b968 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4672,27 +4672,27 @@ public abstract class PackageManager { @PermissionGroupInfoFlags int flags); /** - * Get the platform permissions which belong to a particular permission group. + * Get the platform-defined permissions which belong to a particular permission group. * - * @param permissionGroupName The permission group whose permissions are desired - * @param executor Executor on which to invoke the callback - * @param callback A callback which will receive a list of the platform permissions in the - * group, or empty if the group is not a valid platform group, or there - * was an exception. + * @param permissionGroupName the permission group whose permissions are desired + * @param executor the {@link Executor} on which to invoke the callback + * @param callback the callback which will receive a list of the platform-defined permissions in + * the group, or empty if the group is not a valid platform-defined permission + * group, or there was an exception */ public void getPlatformPermissionsForGroup(@NonNull String permissionGroupName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<List<String>> callback) {} /** - * Get the platform group of a particular permission, if the permission is a platform - * permission. + * Get the platform-defined permission group of a particular permission, if the permission is a + * platform-defined permission. * - * @param permissionName The permission name whose group is desired - * @param executor Executor on which to invoke the callback - * @param callback A callback which will receive the name of the permission group this - * permission belongs to, or null if it has no group, is not a platform - * permission, or there was an exception. + * @param permissionName the permission whose group is desired + * @param executor the {@link Executor} on which to invoke the callback + * @param callback the callback which will receive the name of the permission group this + * permission belongs to, or {@code null} if it has no group, is not a + * platform-defined permission, or there was an exception */ public void getGroupOfPlatformPermission(@NonNull String permissionName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<String> callback) {} diff --git a/core/java/android/ddm/DdmHandle.java b/core/java/android/ddm/DdmHandle.java new file mode 100644 index 000000000000..0505feea9081 --- /dev/null +++ b/core/java/android/ddm/DdmHandle.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.ddm; + +import org.apache.harmony.dalvik.ddmc.ChunkHandler; + +import java.nio.ByteBuffer; + +/** + * Contains utility methods for chunk serialization and deserialization. + */ +public abstract class DdmHandle extends ChunkHandler { + + /** + * Utility function to copy a String out of a ByteBuffer. + * + * This is here because multiple chunk handlers can make use of it, + * and there's nowhere better to put it. + */ + public static String getString(ByteBuffer buf, int len) { + char[] data = new char[len]; + for (int i = 0; i < len; i++) { + data[i] = buf.getChar(); + } + return new String(data); + } + + /** + * Utility function to copy a String into a ByteBuffer. + */ + public static void putString(ByteBuffer buf, String str) { + int len = str.length(); + for (int i = 0; i < len; i++) { + buf.putChar(str.charAt(i)); + } + } + +} diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java index 35da062329b3..e19f19f8fe8c 100644 --- a/core/java/android/ddm/DdmHandleAppName.java +++ b/core/java/android/ddm/DdmHandleAppName.java @@ -30,9 +30,9 @@ import java.nio.ByteBuffer; /** * Track our app name. We don't (currently) handle any inbound packets. */ -public class DdmHandleAppName extends ChunkHandler { +public class DdmHandleAppName extends DdmHandle { - public static final int CHUNK_APNM = type("APNM"); + public static final int CHUNK_APNM = ChunkHandler.type("APNM"); private static volatile Names sNames = new Names("", ""); @@ -51,13 +51,13 @@ public class DdmHandleAppName extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() {} + public void onConnected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() {} + public void onDisconnected() {} /** * Handle a chunk of data. diff --git a/core/java/android/ddm/DdmHandleExit.java b/core/java/android/ddm/DdmHandleExit.java index 74ae37a42f56..a9e3d1607729 100644 --- a/core/java/android/ddm/DdmHandleExit.java +++ b/core/java/android/ddm/DdmHandleExit.java @@ -16,18 +16,20 @@ package android.ddm; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.util.Log; + import java.nio.ByteBuffer; /** * Handle an EXIT chunk. */ -public class DdmHandleExit extends ChunkHandler { +public class DdmHandleExit extends DdmHandle { - public static final int CHUNK_EXIT = type("EXIT"); + public static final int CHUNK_EXIT = ChunkHandler.type("EXIT"); private static DdmHandleExit mInstance = new DdmHandleExit(); @@ -46,13 +48,13 @@ public class DdmHandleExit extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() {} + public void onConnected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() {} + public void onDisconnected() {} /** * Handle a chunk of data. We're only registered for "EXIT". diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java index 8fa235294451..2edb5c70cdf2 100644 --- a/core/java/android/ddm/DdmHandleHeap.java +++ b/core/java/android/ddm/DdmHandleHeap.java @@ -16,21 +16,18 @@ package android.ddm; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import org.apache.harmony.dalvik.ddmc.DdmVmInternal; -import android.os.Debug; -import android.util.Log; -import java.io.IOException; -import java.nio.ByteBuffer; /** * Handle native and virtual heap requests. */ -public class DdmHandleHeap extends ChunkHandler { +public class DdmHandleHeap extends DdmHandle { - public static final int CHUNK_HPGC = type("HPGC"); + public static final int CHUNK_HPGC = ChunkHandler.type("HPGC"); private static DdmHandleHeap mInstance = new DdmHandleHeap(); @@ -49,13 +46,13 @@ public class DdmHandleHeap extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() {} + public void onConnected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() {} + public void onDisconnected() {} /** * Handle a chunk of data. @@ -68,8 +65,7 @@ public class DdmHandleHeap extends ChunkHandler { if (type == CHUNK_HPGC) { return handleHPGC(request); } else { - throw new RuntimeException("Unknown packet " - + ChunkHandler.name(type)); + throw new RuntimeException("Unknown packet " + name(type)); } } diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index 60dfc8d7ee7b..41600294bfb5 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -31,11 +31,11 @@ import java.nio.ByteBuffer; /** * Handle "hello" messages and feature discovery. */ -public class DdmHandleHello extends ChunkHandler { +public class DdmHandleHello extends DdmHandle { - public static final int CHUNK_HELO = type("HELO"); - public static final int CHUNK_WAIT = type("WAIT"); - public static final int CHUNK_FEAT = type("FEAT"); + public static final int CHUNK_HELO = ChunkHandler.type("HELO"); + public static final int CHUNK_WAIT = ChunkHandler.type("WAIT"); + public static final int CHUNK_FEAT = ChunkHandler.type("FEAT"); private static final int CLIENT_PROTOCOL_VERSION = 1; @@ -61,15 +61,14 @@ public class DdmHandleHello extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() { + public void onConnected() { if (false) Log.v("ddm-hello", "Connected!"); if (false) { /* test spontaneous transmission */ byte[] data = new byte[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 127 }; - Chunk testChunk = - new Chunk(ChunkHandler.type("TEST"), data, 1, data.length-2); + Chunk testChunk = new Chunk(ChunkHandler.type("TEST"), data, 1, data.length - 2); DdmServer.sendChunk(testChunk); } } @@ -78,7 +77,7 @@ public class DdmHandleHello extends ChunkHandler { * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() { + public void onDisconnected() { if (false) Log.v("ddm-hello", "Disconnected!"); } @@ -96,8 +95,7 @@ public class DdmHandleHello extends ChunkHandler { } else if (type == CHUNK_FEAT) { return handleFEAT(request); } else { - throw new RuntimeException("Unknown packet " - + ChunkHandler.name(type)); + throw new RuntimeException("Unknown packet " + name(type)); } } diff --git a/core/java/android/ddm/DdmHandleNativeHeap.java b/core/java/android/ddm/DdmHandleNativeHeap.java index 775c57027f78..dfd451c3ab4e 100644 --- a/core/java/android/ddm/DdmHandleNativeHeap.java +++ b/core/java/android/ddm/DdmHandleNativeHeap.java @@ -16,17 +16,18 @@ package android.ddm; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.util.Log; /** * Handle thread-related traffic. */ -public class DdmHandleNativeHeap extends ChunkHandler { +public class DdmHandleNativeHeap extends DdmHandle { - public static final int CHUNK_NHGT = type("NHGT"); + public static final int CHUNK_NHGT = ChunkHandler.type("NHGT"); private static DdmHandleNativeHeap mInstance = new DdmHandleNativeHeap(); @@ -45,13 +46,13 @@ public class DdmHandleNativeHeap extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() {} + public void onConnected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() {} + public void onDisconnected() {} /** * Handle a chunk of data. @@ -63,8 +64,7 @@ public class DdmHandleNativeHeap extends ChunkHandler { if (type == CHUNK_NHGT) { return handleNHGT(request); } else { - throw new RuntimeException("Unknown packet " - + ChunkHandler.name(type)); + throw new RuntimeException("Unknown packet " + name(type)); } } diff --git a/core/java/android/ddm/DdmHandleProfiling.java b/core/java/android/ddm/DdmHandleProfiling.java index cce4dd2005a8..806e4bd96f9c 100644 --- a/core/java/android/ddm/DdmHandleProfiling.java +++ b/core/java/android/ddm/DdmHandleProfiling.java @@ -16,25 +16,28 @@ package android.ddm; + +import android.os.Debug; +import android.util.Log; + import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; -import android.os.Debug; -import android.util.Log; + import java.nio.ByteBuffer; /** * Handle profiling requests. */ -public class DdmHandleProfiling extends ChunkHandler { +public class DdmHandleProfiling extends DdmHandle { - public static final int CHUNK_MPRS = type("MPRS"); - public static final int CHUNK_MPRE = type("MPRE"); - public static final int CHUNK_MPSS = type("MPSS"); - public static final int CHUNK_MPSE = type("MPSE"); - public static final int CHUNK_MPRQ = type("MPRQ"); - public static final int CHUNK_SPSS = type("SPSS"); - public static final int CHUNK_SPSE = type("SPSE"); + public static final int CHUNK_MPRS = ChunkHandler.type("MPRS"); + public static final int CHUNK_MPRE = ChunkHandler.type("MPRE"); + public static final int CHUNK_MPSS = ChunkHandler.type("MPSS"); + public static final int CHUNK_MPSE = ChunkHandler.type("MPSE"); + public static final int CHUNK_MPRQ = ChunkHandler.type("MPRQ"); + public static final int CHUNK_SPSS = ChunkHandler.type("SPSS"); + public static final int CHUNK_SPSE = ChunkHandler.type("SPSE"); private static final boolean DEBUG = false; private static DdmHandleProfiling mInstance = new DdmHandleProfiling(); @@ -60,13 +63,13 @@ public class DdmHandleProfiling extends ChunkHandler { * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ - public void connected() {} + public void onConnected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ - public void disconnected() {} + public void onDisconnected() {} /** * Handle a chunk of data. @@ -91,8 +94,7 @@ public class DdmHandleProfiling extends ChunkHandler { } else if (type == CHUNK_SPSE) { return handleMPSEOrSPSE(request, "Sample"); } else { - throw new RuntimeException("Unknown packet " - + ChunkHandler.name(type)); + throw new RuntimeException("Unknown packet " + name(type)); } } diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java index 5539dc92f82a..6b0f78f2c1c3 100644 --- a/core/java/android/ddm/DdmHandleViewDebug.java +++ b/core/java/android/ddm/DdmHandleViewDebug.java @@ -39,12 +39,12 @@ import java.nio.ByteBuffer; * Handle various requests related to profiling / debugging of the view system. * Support for these features are advertised via {@link DdmHandleHello}. */ -public class DdmHandleViewDebug extends ChunkHandler { +public class DdmHandleViewDebug extends DdmHandle { /** List {@link ViewRootImpl}'s of this process. */ - private static final int CHUNK_VULW = type("VULW"); + private static final int CHUNK_VULW = ChunkHandler.type("VULW"); /** Operation on view root, first parameter in packet should be one of VURT_* constants */ - private static final int CHUNK_VURT = type("VURT"); + private static final int CHUNK_VURT = ChunkHandler.type("VURT"); /** Dump view hierarchy. */ private static final int VURT_DUMP_HIERARCHY = 1; @@ -59,7 +59,7 @@ public class DdmHandleViewDebug extends ChunkHandler { * Generic View Operation, first parameter in the packet should be one of the * VUOP_* constants below. */ - private static final int CHUNK_VUOP = type("VUOP"); + private static final int CHUNK_VUOP = ChunkHandler.type("VUOP"); /** Capture View. */ private static final int VUOP_CAPTURE_VIEW = 1; @@ -99,11 +99,11 @@ public class DdmHandleViewDebug extends ChunkHandler { } @Override - public void connected() { + public void onConnected() { } @Override - public void disconnected() { + public void onDisconnected() { } @Override @@ -154,7 +154,7 @@ public class DdmHandleViewDebug extends ChunkHandler { return createFailChunk(ERR_INVALID_OP, "Unknown view operation: " + op); } } else { - throw new RuntimeException("Unknown packet " + ChunkHandler.name(type)); + throw new RuntimeException("Unknown packet " + name(type)); } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f0d410f46f81..881e0cfb58d7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -481,6 +481,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean mFullscreenApplied; boolean mIsFullscreen; + private boolean mLastWasInFullscreenMode; @UnsupportedAppUsage View mExtractView; boolean mExtractViewHidden; @@ -920,8 +921,17 @@ public class InputMethodService extends AbstractInputMethodService { if (mHandler == null) { mHandler = new Handler(getMainLooper()); } - mImeSurfaceScheduledForRemoval = true; - mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + + if (mLastWasInFullscreenMode) { + // Caching surface / delaying surface removal can cause mServedView to detach in certain + // cases in RecyclerView (b/187772544). + // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching + // view issues is resolved in RecyclerView. + removeImeSurface(); + } else { + mImeSurfaceScheduledForRemoval = true; + mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS); + } } private void removeImeSurface() { @@ -2350,6 +2360,7 @@ public class InputMethodService extends AbstractInputMethodService { onWindowHidden(); mDecorViewWasVisible = false; } + mLastWasInFullscreenMode = mIsFullscreen; updateFullscreenMode(); } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 370052d47d16..f2857ceb59d7 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -368,27 +368,19 @@ public final class BatteryUsageStats implements Parcelable { }; /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */ - public byte[] getStatsProto(long sessionEndTimestampMs) { - - final long sessionStartMillis = getStatsStartTimestamp(); - // TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added. - final long sessionEndMillis = sessionEndTimestampMs; - final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp(); - + public byte[] getStatsProto() { final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer( AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - final int sessionDischargePercentage = getDischargePercentage(); - final ProtoOutputStream proto = new ProtoOutputStream(); - proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis); - proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis); - proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis); + proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp()); + proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp()); + proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration()); deviceBatteryConsumer.writeStatsProto(proto, BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); writeUidBatteryConsumersProto(proto); proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE, - sessionDischargePercentage); + getDischargePercentage()); return proto.getBytes(); } @@ -399,8 +391,8 @@ public final class BatteryUsageStats implements Parcelable { private void writeUidBatteryConsumersProto(ProtoOutputStream proto) { final List<UidBatteryConsumer> consumers = getUidBatteryConsumers(); - // TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb, - // truncate the remainder of the list. + // TODO(b/189225426): Sort the list by power consumption. If during the for, + // proto.getRawSize() > 45kb, truncate the remainder of the list. final int size = consumers.size(); for (int i = 0; i < size; i++) { final UidBatteryConsumer consumer = consumers.get(i); diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java index 26fc6f0ae4ce..2539a6b2a0eb 100644 --- a/core/java/android/os/HidlMemory.java +++ b/core/java/android/os/HidlMemory.java @@ -79,6 +79,7 @@ public class HidlMemory implements Closeable { public void close() throws IOException { if (mHandle != null) { mHandle.close(); + mHandle = null; } } diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java index 01cece39b922..c82a51669b86 100644 --- a/core/java/android/os/VibratorManager.java +++ b/core/java/android/os/VibratorManager.java @@ -94,6 +94,8 @@ public abstract class VibratorManager { * VibrationEffect VibrationEffects} to be played on one or more vibrators. * </p> * + * <p>The app should be in foreground for the vibration to happen.</p> + * * @param effect a combination of effects to be performed by one or more vibrators. */ @RequiresPermission(android.Manifest.permission.VIBRATE) @@ -109,6 +111,9 @@ public abstract class VibratorManager { * VibrationEffect} to be played on one or more vibrators. * </p> * + * <p>The app should be in foreground for the vibration to happen. Background apps should + * specify a ringtone, notification or alarm usage in order to vibrate.</p> + * * @param effect a combination of effects to be performed by one or more vibrators. * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example, * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0acacb60da3f..908d236c6c02 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9828,30 +9828,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mContext.getContentCaptureOptions() == null) return; if (appeared) { - if (!isLaidOut() || getVisibility() != VISIBLE - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) { + // The appeared event stops sending to AiAi. + // 1. The view is hidden. + // 2. The same event was sent. + // 3. The view is not laid out, and it will be laid out in the future. + // Some recycled views cached its layout and a relayout is unnecessary. In this case, + // system still needs to notify content capture the view appeared. When a view is + // recycled, it will set the flag PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED. + final boolean isRecycledWithoutRelayout = getNotifiedContentCaptureDisappeared() + && getVisibility() == VISIBLE + && !isLayoutRequested(); + if (getVisibility() != VISIBLE || getNotifiedContentCaptureAppeared() + || !(isLaidOut() || isRecycledWithoutRelayout)) { if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared() + + ", alreadyNotifiedDisappeared=" + + getNotifiedContentCaptureDisappeared()); } return; } } else { - if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 - || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { + if (!getNotifiedContentCaptureAppeared() || getNotifiedContentCaptureDisappeared()) { if (DEBUG_CONTENT_CAPTURE) { Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid=" + isLaidOut() + ", visibleToUser=" + isVisibleToUser() + ", visible=" + (getVisibility() == VISIBLE) - + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) - + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4 - & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0)); + + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared() + + ", alreadyNotifiedDisappeared=" + + getNotifiedContentCaptureDisappeared()); } return; } @@ -9899,6 +9906,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } + private boolean getNotifiedContentCaptureDisappeared() { + return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0; + } + /** * Sets the (optional) {@link ContentCaptureSession} associated with this view. * diff --git a/core/java/android/view/translation/TranslationResponseValue.java b/core/java/android/view/translation/TranslationResponseValue.java index a68ae5658b37..e2ef5d341c82 100644 --- a/core/java/android/view/translation/TranslationResponseValue.java +++ b/core/java/android/view/translation/TranslationResponseValue.java @@ -49,7 +49,24 @@ public final class TranslationResponseValue implements Parcelable { * <p>The dictionary definitions consists of groups of terms keyed by their corresponding parts * of speech. This map-like structure is stored in a {@link Bundle}. The individual parts of * speech can be traversed by {@link Bundle#keySet()} and used to get the corresponding list - * of terms as {@link CharSequence}s.</p> + * of terms as {@link CharSequence}s. + * + * <ul> + * <li>"noun" -> ["def1", "def2", ...]</li> + * <li>"verb" -> ["def3", "def4", ...]</li> + * <li>...</li> + * </ul> + * + * The set of parts of speech can then be used by + * {@link Bundle#getCharSequenceArrayList(String)} to get the list of terms. + * + * <b>Example</b>: + * + * {@code for (String partOfSpeech : extras.getBundle(EXTRA_DEFINITIONS).keySet()) { + * ArrayList<CharSequence> terms = + * extras.getBundle(EXTRA_DEFINITIONS).getCharSequenceArrayList(partOfSpeech); + * ... + * }}</p> */ public static final String EXTRA_DEFINITIONS = "android.view.translation.extra.DEFINITIONS"; @@ -70,7 +87,8 @@ public final class TranslationResponseValue implements Parcelable { /** * Extra results associated with the translated text. * - * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p> + * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}. + * </p> */ @NonNull private final Bundle mExtras; @@ -190,7 +208,8 @@ public final class TranslationResponseValue implements Parcelable { /** * Extra results associated with the translated text. * - * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p> + * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}. + * </p> */ @DataClass.Generated.Member public @NonNull Bundle getExtras() { @@ -370,7 +389,8 @@ public final class TranslationResponseValue implements Parcelable { /** * Extra results associated with the translated text. * - * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}.</p> + * <p>The bundle includes {@link #EXTRA_DEFINITIONS}, obtained by {@link Bundle#getBundle}. + * </p> */ @DataClass.Generated.Member public @NonNull Builder setExtras(@NonNull Bundle value) { @@ -423,7 +443,7 @@ public final class TranslationResponseValue implements Parcelable { } @DataClass.Generated( - time = 1621034223313L, + time = 1621623218037L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponseValue.java", inputSignatures = "public static final int STATUS_SUCCESS\npublic static final int STATUS_ERROR\npublic static final java.lang.String EXTRA_DEFINITIONS\nprivate final @android.view.translation.TranslationResponseValue.Status int mStatusCode\nprivate final @android.annotation.Nullable java.lang.CharSequence mText\nprivate final @android.annotation.NonNull android.os.Bundle mExtras\nprivate final @android.annotation.Nullable java.lang.CharSequence mTransliteration\npublic static @android.annotation.NonNull android.view.translation.TranslationResponseValue forError()\nprivate static java.lang.CharSequence defaultText()\nprivate static android.os.Bundle defaultExtras()\nprivate static java.lang.CharSequence defaultTransliteration()\nclass TranslationResponseValue extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)\nclass BaseBuilder extends java.lang.Object implements []") diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 0d041c4440d2..e693d9dd9a7d 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -201,7 +201,10 @@ public class CpuPowerCalculator extends PowerCalculator { result.packageWithHighestDrain = packageWithHighestDrain; } - private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { + /** + * Calculates CPU power consumed by the specified app, using the PowerProfile model. + */ + public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { // Constant battery drain when CPU is active double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index a26abc2ffab0..b4a2b637cae8 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -40,8 +40,10 @@ public class SystemServicePowerCalculator extends PowerCalculator { // to this layout: // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...} private final UsageBasedPowerEstimator[] mPowerEstimators; + private final CpuPowerCalculator mCpuPowerCalculator; public SystemServicePowerCalculator(PowerProfile powerProfile) { + mCpuPowerCalculator = new CpuPowerCalculator(powerProfile); int numFreqs = 0; final int numCpuClusters = powerProfile.getNumCpuClusters(); for (int cluster = 0; cluster < numCpuClusters; cluster++) { @@ -62,7 +64,22 @@ public class SystemServicePowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - double systemServicePowerMah = calculateSystemServicePower(batteryStats); + final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); + if (systemUid == null) { + return; + } + + final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); + final int powerModel = getPowerModel(consumptionUC, query); + + double systemServicePowerMah; + if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, + systemUid, consumptionUC); + } else { + systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); + } + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = builder.getUidBatteryConsumerBuilders(); final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get( @@ -76,7 +93,7 @@ public class SystemServicePowerCalculator extends PowerCalculator { // distributed to applications systemServerConsumer.setConsumedPower( BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, - -systemServicePowerMah); + -systemServicePowerMah, powerModel); } for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { @@ -84,7 +101,8 @@ public class SystemServicePowerCalculator extends PowerCalculator { if (app != systemServerConsumer) { final BatteryStats.Uid uid = app.getBatteryStatsUid(); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, - systemServicePowerMah * uid.getProportionalSystemServiceUsage()); + systemServicePowerMah * uid.getProportionalSystemServiceUsage(), + powerModel); } } @@ -102,7 +120,20 @@ public class SystemServicePowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - double systemServicePowerMah = calculateSystemServicePower(batteryStats); + final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID); + if (systemUid == null) { + return; + } + + final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC(); + double systemServicePowerMah; + if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { + systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats, + systemUid, consumptionUC); + } else { + systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats); + } + BatterySipper systemServerSipper = null; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper app = sippers.get(i); @@ -134,7 +165,21 @@ public class SystemServicePowerCalculator extends PowerCalculator { } } - private double calculateSystemServicePower(BatteryStats batteryStats) { + private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats, + BatteryStats.Uid systemUid, long consumptionUC) { + // Use the PowerProfile based model to estimate the ratio between the power consumed + // while handling incoming binder calls and the entire System UID power consumption. + // Apply that ratio to the _measured_ system UID power consumption to get a more + // accurate estimate of the power consumed by incoming binder calls. + final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats); + final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah( + systemUid, BatteryStats.STATS_SINCE_CHARGED); + + return uCtoMah(consumptionUC) * systemServiceModeledPowerMah + / systemUidModeledPowerMah; + } + + private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) { final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); if (systemServiceTimeAtCpuSpeeds == null) { return 0; @@ -145,13 +190,12 @@ public class SystemServicePowerCalculator extends PowerCalculator { double powerMah = 0; final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length); for (int i = 0; i < size; i++) { - powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]); + powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000); } if (DEBUG) { Log.d(TAG, "System service power:" + powerMah); } - return powerMah; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 084d4d1c0c42..f457c5626fba 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2890,7 +2890,13 @@ <permission android:name="android.permission.SET_DISPLAY_OFFSET" android:protectionLevel="signature|privileged" /> - <!-- Allows a companion app to run in the background. + <!-- Allows a companion app to run in the background. This permission implies + {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND}, + and allows to start a foreground service from the background. + If an app does not have to run in the background, but only needs to start a foreground + service from the background, consider using + {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND}, + which is less powerful. <p>Protection level: normal --> <permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" @@ -2900,6 +2906,7 @@ <!-- Allows a companion app to start a foreground service from the background. {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} + <p>Protection level: normal --> <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND" android:protectionLevel="normal"/> diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java index bee0a0bf1fd6..333eebb86d4c 100644 --- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java +++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java @@ -48,9 +48,8 @@ public class BatteryUsageStatsPulledTest { @Test public void testGetStatsProto() { - final long sessionEndTimestampMs = 1050; final BatteryUsageStats bus = buildBatteryUsageStats(); - final byte[] bytes = bus.getStatsProto(sessionEndTimestampMs); + final byte[] bytes = bus.getStatsProto(); BatteryUsageStatsAtomsProto proto; try { proto = BatteryUsageStatsAtomsProto.parseFrom(bytes); @@ -60,9 +59,9 @@ public class BatteryUsageStatsPulledTest { } assertEquals(bus.getStatsStartTimestamp(), proto.sessionStartMillis); - assertEquals(sessionEndTimestampMs, proto.sessionEndMillis); + assertEquals(bus.getStatsEndTimestamp(), proto.sessionEndMillis); assertEquals( - sessionEndTimestampMs - bus.getStatsStartTimestamp(), + bus.getStatsEndTimestamp() - bus.getStatsStartTimestamp(), proto.sessionDurationMillis); assertEquals(bus.getDischargePercentage(), proto.sessionDischargePercentage); diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index cd45060b7cc4..a36d9fe94b46 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -18,25 +18,28 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.os.BatteryConsumer; -import android.os.BatteryStats; -import android.os.BatteryUsageStatsQuery; import android.os.Binder; import android.os.Process; -import android.os.UidBatteryConsumer; -import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.power.MeasuredEnergyStats; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +50,8 @@ import java.util.Collection; public class SystemServicePowerCalculatorTest { private static final double PRECISION = 0.000001; + private static final int APP_UID1 = 100; + private static final int APP_UID2 = 200; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() @@ -61,73 +66,52 @@ public class SystemServicePowerCalculatorTest { .setAveragePowerForCpuCore(1, 0, 500) .setAveragePowerForCpuCore(1, 1, 600); + @Mock private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader; + @Mock + private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader; + @Mock + private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader; + + private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{ + mock(KernelCpuSpeedReader.class), + mock(KernelCpuSpeedReader.class), + }; + private MockBatteryStatsImpl mMockBatteryStats; - private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; - private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader; @Before public void setUp() throws IOException { - mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class); - mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader(); - mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); + MockitoAnnotations.initMocks(this); mMockBatteryStats = mStatsRule.getBatteryStats() - .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) + .setUserInfoProvider(mMockUserInfoProvider) + .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders) .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) - .setUserInfoProvider(mMockUserInfoProvider); + .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader) + .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader) + .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader) + .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader); } @Test public void testPowerProfileBasedModel() { - when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); - - // Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total - mMockSystemServerCpuThreadReader.setCpuTimes( - new long[] {30000, 40000, 50000, 60000}, - new long[] {20000, 30000, 40000, 50000}); - - mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( - new long[] {10000, 20000, 30000, 40000} - ); - - mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false, null); - - int workSourceUid1 = 100; - int workSourceUid2 = 200; - int transactionCode = 42; - - Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); - BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1, - Binder.class, transactionCode, true /*screenInteractive */); - stat1.incrementalCallCount = 100; - stat1.recordedCallCount = 100; - stat1.cpuTimeMicros = 1000000; - callStats.add(stat1); - - mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats); - - callStats.clear(); - BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, - Binder.class, transactionCode, true /*screenInteractive */); - stat2.incrementalCallCount = 100; - stat2.recordedCallCount = 100; - stat2.cpuTimeMicros = 9000000; - callStats.add(stat2); - - mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats); - - mMockBatteryStats.updateSystemServiceCallStats(); - mMockBatteryStats.updateSystemServerThreadStats(); + prepareBatteryStats(null); SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( mStatsRule.getPowerProfile()); - mStatsRule.apply(new FakeCpuPowerCalculator(), calculator); + mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator); - assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1) + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .isWithin(PRECISION).of(1.888888); - assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2) + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2) .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) .isWithin(PRECISION).of(17.0); assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) @@ -141,58 +125,105 @@ public class SystemServicePowerCalculatorTest { .isWithin(PRECISION).of(18.888888); } - private static class MockKernelCpuUidFreqTimeReader extends - KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { - private long[] mSystemServerCpuTimes; - - MockKernelCpuUidFreqTimeReader() { - super(/*throttle */false); - } - - void setSystemServerCpuTimes(long[] systemServerCpuTimes) { - mSystemServerCpuTimes = systemServerCpuTimes; - } - - @Override - public boolean perClusterTimesAvailable() { - return true; - } - - @Override - public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) { - if (cb != null) { - cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); - } - } - } + @Test + public void testMeasuredEnergyBasedModel() { + final boolean[] supportedPowerBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + mStatsRule.getBatteryStats() + .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]); - private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader { - private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + prepareBatteryStats(new long[]{50000000, 100000000}); - MockSystemServerCpuThreadReader() { - super(null); - } + SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( + mStatsRule.getPowerProfile()); - public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { - mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; - mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; - } + mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator); - @Override - public SystemServiceCpuThreadTimes readDelta() { - return mThreadTimes; - } + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(1.979351); + assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(17.814165); + assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS)) + .isWithin(PRECISION).of(-19.793517); + assertThat(mStatsRule.getDeviceBatteryConsumer() + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(19.793517); + assertThat(mStatsRule.getAppsBatteryConsumer() + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(19.793517); } - private static class FakeCpuPowerCalculator extends PowerCalculator { - @Override - protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, - long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - if (u.getUid() == Process.SYSTEM_UID) { - // SystemServer must be attributed at least as much power as the total - // of all system services requested by apps. - app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000); - } - } + private void prepareBatteryStats(long[] clusterChargesUc) { + when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); + + when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000}); + when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000}); + + when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false); + + // User/System CPU time in microseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000}); + callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000}); + callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000}); + return null; + }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any()); + + // Active CPU time in milliseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, 1111L); + callback.onUidCpuTime(APP_UID2, 3333L); + callback.onUidCpuTime(Process.SYSTEM_UID, 10000L); + return null; + }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any()); + + // Per-cluster CPU time in milliseconds + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222}); + callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444}); + callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000}); + return null; + }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any()); + + // System service CPU time + final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes = + new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes(); + threadTimes.binderThreadCpuTimesUs = + new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000}; + + when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes); + + int transactionCode = 42; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1, + Binder.class, transactionCode, true /*screenInteractive */); + stat1.incrementalCallCount = 100; + stat1.recordedCallCount = 100; + stat1.cpuTimeMicros = 1_000_000; + callStats.add(stat1); + + mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2, + Binder.class, transactionCode, true /*screenInteractive */); + stat2.incrementalCallCount = 100; + stat2.recordedCallCount = 100; + stat2.cpuTimeMicros = 9_000_000; + callStats.add(stat2); + + mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats); + + mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc); + + mMockBatteryStats.prepareForDumpLocked(); } } diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt index 52ee63a15a63..be21f4e87101 100644 --- a/framework-jarjar-rules.txt +++ b/framework-jarjar-rules.txt @@ -1,3 +1,7 @@ +# This rule is meant to be reused across libraries in the bootclasspath that depend +# on the HIDL libraries. rule android.hidl.** android.internal.hidl.@1 + +# Framework-specific renames. rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1 rule com.android.server.vcn.util.** com.android.server.vcn.repackaged.util.@1 diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index be45f1813a0e..7ad46a161283 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -975,7 +975,7 @@ public class RippleDrawable extends LayerDrawable { shader.setColor(color, effectColor); shader.setOrigin(cx, cy); shader.setTouch(x, y); - shader.setResolution(w, h, mState.mDensity); + shader.setResolution(w, h); shader.setNoisePhase(noisePhase); shader.setRadius(radius); shader.setProgress(.0f); @@ -1193,6 +1193,9 @@ public class RippleDrawable extends LayerDrawable { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); + if (mState.mRippleStyle == STYLE_PATTERNED) { + mRipplePaint.setDither(true); + } } final float x = mHotspotBounds.exactCenterX(); diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index eb726c1dad34..57b322334867 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -20,7 +20,6 @@ import android.annotation.ColorInt; import android.graphics.Color; import android.graphics.RuntimeShader; import android.graphics.Shader; -import android.util.DisplayMetrics; final class RippleShader extends RuntimeShader { private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n" @@ -204,8 +203,8 @@ final class RippleShader extends RuntimeShader { sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()}); } - public void setResolution(float w, float h, int density) { - final float densityScale = density * DisplayMetrics.DENSITY_DEFAULT_SCALE * 0.8f; + public void setResolution(float w, float h) { + final float densityScale = 2.1f; setUniform("in_resolutionScale", new float[] {1f / w, 1f / h}); setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h}); } diff --git a/location/java/android/location/LocationDeviceConfig.java b/location/java/android/location/LocationDeviceConfig.java index 92845745828b..c55eed9211f7 100644 --- a/location/java/android/location/LocationDeviceConfig.java +++ b/location/java/android/location/LocationDeviceConfig.java @@ -16,16 +16,11 @@ package android.location; -import android.annotation.SystemApi; -import android.annotation.TestApi; - /** * DeviceConfig keys within the location namespace. * * @hide */ -@SystemApi -@TestApi public final class LocationDeviceConfig { /** diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index f83dc407d870..ae44c5e34521 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1823,6 +1823,7 @@ public class LocationManager { * @deprecated Use {@link #isProviderPackage(String, String, String)} instead. * * @hide + * @removed */ @Deprecated @SystemApi diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 52d2b3c919d9..8f3e4bd87aa7 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -19,7 +19,6 @@ package com.android.settingslib.drawer; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; -import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI; @@ -301,16 +300,8 @@ public abstract class Tile implements Parcelable { } int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON); - // Set the icon - if (iconResId == 0) { - // Only fallback to componentInfo.icon if metadata does not contain ICON_URI. - // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this - // level is too complex because we don't have a strong threading contract for this class - if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) { - iconResId = getComponentIcon(componentInfo); - } - } - if (iconResId != 0) { + // Set the icon. Skip the transparent color for backward compatibility since Android S. + if (iconResId != 0 && iconResId != android.R.color.transparent) { final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId); if (isIconTintable(context)) { final TypedArray a = context.obtainStyledAttributes(new int[]{ diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java index bd0b9e93b09d..6cb60d1aaf0e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java @@ -98,7 +98,7 @@ public class MetricsFeatureProvider { /** * Logs a simple action without page id or attribution */ - public void action(Context context, int category, Pair<Integer, Object>... taggedData) { + public void action(Context context, int category, Pair<Integer, Object>... taggedData) { for (LogWriter writer : mLoggerWriters) { writer.action(context, category, taggedData); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java index 4f8ecf8f8823..aa6b0bf33b69 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/ActivityTileTest.java @@ -105,11 +105,10 @@ public class ActivityTileTest { } @Test - public void getIcon_noIconMetadata_returnActivityIcon() { - mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, 0); + public void getIcon_transparentColorInMetadata_returnNull() { + mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.color.transparent); - assertThat(mTile.getIcon(RuntimeEnvironment.application).getResId()) - .isEqualTo(mActivityInfo.icon); + assertThat(mTile.getIcon(RuntimeEnvironment.application)).isNull(); } @Test diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index b3c437480af7..989010e8730c 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -114,6 +114,11 @@ public interface BcSmartspaceDataPlugin extends Plugin { * Set or clear next alarm information */ void setNextAlarm(@Nullable Drawable image, @Nullable String description); + + /** + * Set or clear device media playing + */ + void setMediaTarget(@Nullable SmartspaceTarget target); } /** Interface for launching Intents, which can differ on the lockscreen */ diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index b08e51372dd0..f1cda277f045 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -126,6 +126,7 @@ android:gravity="right" android:layout_gravity="right" android:clipToPadding="false" + android:clipToOutline="true" android:background="@drawable/volume_row_rounded_background"> <com.android.systemui.volume.CaptionsToggleImageButton android:id="@+id/odi_captions_icon" @@ -146,6 +147,7 @@ android:layout="@layout/volume_tool_tip_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="bottom | right"/> + android:layout_gravity="bottom | right" + android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml index 87acfd088939..52132e881c43 100644 --- a/packages/SystemUI/res/layout/media_carousel.xml +++ b/packages/SystemUI/res/layout/media_carousel.xml @@ -22,6 +22,7 @@ android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" + android:forceHasOverlappingRendering="false" android:theme="@style/MediaPlayer"> <com.android.systemui.media.MediaScrollView android:id="@+id/media_carousel_scroller" diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index a2a14c754e98..5f59e781ef5e 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -22,11 +22,15 @@ android:id="@+id/navigation_bar_view" android:layout_height="match_parent" android:layout_width="match_parent" + android:clipChildren="false" + android:clipToPadding="false" android:background="@drawable/system_bar_background"> <com.android.systemui.navigationbar.NavigationBarInflaterView android:id="@+id/navigation_inflater" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" /> </com.android.systemui.navigationbar.NavigationBarView> diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml index 64c7422c27ed..46f238cbe588 100644 --- a/packages/SystemUI/res/layout/navigation_layout.xml +++ b/packages/SystemUI/res/layout/navigation_layout.xml @@ -23,6 +23,8 @@ android:layout_marginEnd="@dimen/rounded_corner_content_padding" android:paddingStart="@dimen/nav_content_padding" android:paddingEnd="@dimen/nav_content_padding" + android:clipChildren="false" + android:clipToPadding="false" android:id="@+id/horizontal"> <com.android.systemui.navigationbar.buttons.NearestTouchFrame diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml index e294dad6ce34..b7e86a3b0b84 100644 --- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml @@ -14,6 +14,7 @@ ~ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" @@ -52,7 +53,7 @@ android:paddingStart="8dp" android:paddingEnd="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="14sp" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml index 47cab423b0ad..1086a1361366 100644 --- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml @@ -16,6 +16,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:id="@+id/item" android:background="@drawable/people_space_tile_view_card" @@ -128,7 +129,7 @@ android:paddingStart="8dp" android:paddingEnd="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="14sp" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml index 7a1371d034b6..22fcd3bcce37 100644 --- a/packages/SystemUI/res/layout/people_tile_small.xml +++ b/packages/SystemUI/res/layout/people_tile_small.xml @@ -14,6 +14,7 @@ ~ limitations under the License. --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -50,7 +51,7 @@ android:gravity="center" android:paddingHorizontal="8dp" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" + android:textColor="?androidprv:attr/textColorOnAccent" android:background="@drawable/people_space_messages_count_background" android:textSize="@dimen/name_text_size_for_small" android:maxLines="1" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index c16f13ef5ae6..fb39d3e4027d 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -129,7 +129,22 @@ android:layout_marginTop="@dimen/status_bar_header_height_keyguard" android:text="@string/report_rejected_touch" android:visibility="gone" /> - + <com.android.systemui.statusbar.phone.TapAgainView + android:id="@+id/shade_falsing_tap_again" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + systemui:layout_constraintLeft_toLeftOf="parent" + systemui:layout_constraintRight_toRightOf="parent" + systemui:layout_constraintBottom_toBottomOf="parent" + android:layout_marginBottom="20dp" + 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> <FrameLayout diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index beac05794fe9..51718d9af054 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -125,6 +125,7 @@ android:gravity="right" android:layout_gravity="right" android:clipToPadding="false" + android:clipToOutline="true" android:background="@drawable/volume_row_rounded_background"> <com.android.systemui.volume.CaptionsToggleImageButton android:id="@+id/odi_captions_icon" @@ -146,7 +147,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom | right" - android:layout_marginRight="@dimen/volume_tool_tip_right_margin" - android:layout_marginBottom="@dimen/volume_tool_tip_bottom_margin"/> + android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index 1b0076cae671..ee89b97a4a9c 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -60,6 +60,7 @@ android:thumb="@null" android:splitTrack="false" android:progressDrawable="@drawable/volume_row_seekbar" + android:background="@null" android:layoutDirection="ltr" android:rotation="270" /> </FrameLayout> diff --git a/packages/SystemUI/res/layout/volume_tool_tip_view.xml b/packages/SystemUI/res/layout/volume_tool_tip_view.xml index 9fe885ebefc7..ee2496948752 100644 --- a/packages/SystemUI/res/layout/volume_tool_tip_view.xml +++ b/packages/SystemUI/res/layout/volume_tool_tip_view.xml @@ -17,6 +17,7 @@ <com.android.systemui.volume.VolumeToolTipView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:id="@+id/tooltip_view" android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -35,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:textColor="@android:color/white" + android:textColor="?android:attr/textColorPrimaryInverse" android:text="@string/volume_odi_captions_tip" android:textSize="14sp"/> <ImageView @@ -48,7 +49,7 @@ android:layout_marginEnd="2dp" android:alpha="0.7" android:src="@drawable/ic_remove_no_shadow" - android:tint="@android:color/white" + android:tint="?android:attr/textColorPrimaryInverse" android:background="?android:attr/selectableItemBackgroundBorderless" android:contentDescription="@string/accessibility_volume_close_odi_captions_tip"/> </LinearLayout> diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml index 220ed5cce83e..a9bc9e5cd638 100644 --- a/packages/SystemUI/res/values-land-television/dimens.xml +++ b/packages/SystemUI/res/values-land-television/dimens.xml @@ -23,6 +23,7 @@ <dimen name="volume_dialog_slider_width">4dp</dimen> <dimen name="volume_dialog_slider_corner_radius">@dimen/volume_dialog_slider_width</dimen> <dimen name="volume_dialog_background_blur_radius">100dp</dimen> + <dimen name="volume_tool_tip_right_margin">136dp</dimen> <dimen name="tv_volume_dialog_bubble_size">36dp</dimen> <dimen name="tv_volume_dialog_row_padding">6dp</dimen> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 215698dd1884..9df9db607b9f 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -43,7 +43,6 @@ <dimen name="qs_detail_margin_top">14dp</dimen> - <dimen name="volume_tool_tip_right_margin">136dp</dimen> <dimen name="volume_tool_tip_top_margin">12dp</dimen> <dimen name="volume_row_slider_height">128dp</dimen> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d4dbcf969a73..be9065b2c13b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -352,7 +352,7 @@ <!-- Nav bar button default ordering/layout --> <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]</string> <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]</string> - <string name="config_navBarLayoutHandle" translatable="false">back[40AC];home_handle;ime_switcher[40AC]</string> + <string name="config_navBarLayoutHandle" translatable="false">back[70AC];home_handle;ime_switcher[70AC]</string> <bool name="quick_settings_show_full_alarm">false</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b2ab5f782d71..5c1e9355b650 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1418,10 +1418,6 @@ <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen> <dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen> - <!-- Delay after which the media will start transitioning to the full shade on - the lockscreen --> - <dimen name="lockscreen_shade_media_transition_start_delay">40dp</dimen> - <!-- Distance that the full shade transition takes in order for qs to fully transition to the shade --> <dimen name="lockscreen_shade_qs_transition_distance">200dp</dimen> @@ -1430,13 +1426,16 @@ the shade (in alpha) --> <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen> - <!-- Extra inset for the notifications when accounting for media during the lockscreen to - shade transition to compensate for the disappearing media --> - <dimen name="lockscreen_shade_transition_extra_media_inset">-48dp</dimen> + <!-- Distance that the full shade transition takes in order for media to fully transition to + the shade --> + <dimen name="lockscreen_shade_media_transition_distance">140dp</dimen> <!-- Maximum overshoot for the topPadding of notifications when transitioning to the full shade --> - <dimen name="lockscreen_shade_max_top_overshoot">32dp</dimen> + <dimen name="lockscreen_shade_notification_movement">24dp</dimen> + + <!-- Maximum overshoot for the pulse expansion --> + <dimen name="pulse_expansion_max_top_overshoot">16dp</dimen> <dimen name="people_space_widget_radius">28dp</dimen> <dimen name="people_space_image_radius">20dp</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 6393147f8b0b..f4086edf627d 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -38,8 +38,6 @@ <!-- The new animations to/from lockscreen and AOD! --> <bool name="flag_lockscreen_animations">false</bool> - <bool name="flag_toast_style">false</bool> - <bool name="flag_pm_lite">false</bool> <bool name="flag_alarm_tile">false</bool> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 01d0dde6da5e..db3fae6fa491 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1062,6 +1062,9 @@ <!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] --> <string name="notification_tap_again">Tap again to open</string> + <!-- Asks for a second tap as confirmation on an item that normally requires one tap. [CHAR LIMIT=60] --> + <string name="tap_again">Tap again</string> + <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] --> <string name="keyguard_unlock">Swipe up to open</string> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index f98a959346d3..b2ae2a0ca8ad 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -40,7 +40,8 @@ android_library { name: "SystemUISharedLib", srcs: [ "src/**/*.java", - "src/**/I*.aidl", + "src/**/*.kt", + "src/**/*.aidl", ":wm_shell-aidls", ], diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 2cf3ad2dcd49..c468e416f8a5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -36,6 +36,9 @@ import java.util.StringJoiner; * Various shared constants between Launcher and SysUI as part of quickstep */ public class QuickStepContract { + // Fully qualified name of the Launcher activity. + public static final String LAUNCHER_ACTIVITY_CLASS_NAME = + "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; @@ -52,6 +55,8 @@ public class QuickStepContract { // See IStartingWindow.aidl public static final String KEY_EXTRA_SHELL_STARTING_WINDOW = "extra_shell_starting_window"; + // See ISmartspaceTransitionController.aidl + public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition"; public static final String NAV_BAR_MODE_2BUTTON_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl new file mode 100644 index 000000000000..511df4c285b4 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.SmartspaceState; + +// Methods for getting and setting the state of a SmartSpace. This is used to allow a remote process +// (such as System UI) to sync with and control a SmartSpace view hosted in another process (such as +// Launcher). +interface ISmartspaceCallback { + + // Return information about the state of the SmartSpace, including location on-screen and + // currently selected page. + SmartspaceState getSmartspaceState(); + + // Set the currently selected page of this SmartSpace. + oneway void setSelectedPage(int selectedPage); + + oneway void setVisibility(int visibility); +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl new file mode 100644 index 000000000000..2b3e961ab3b2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.ISmartspaceCallback; + +// Controller that keeps track of SmartSpace instances in remote processes (such as Launcher). +interface ISmartspaceTransitionController { + oneway void setSmartspace(ISmartspaceCallback callback); +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl new file mode 100644 index 000000000000..2d01d6af63c5 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace; + +import com.android.systemui.shared.system.smartspace.SmartspaceState; + +parcelable SmartspaceState;
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt new file mode 100644 index 000000000000..2d51c4d13611 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace + +import android.graphics.Rect +import android.os.Parcel +import android.os.Parcelable + +/** + * Represents the state of a SmartSpace, including its location on screen and the index of the + * currently selected page. This object contains all of the information needed to synchronize two + * SmartSpace instances so that we can perform shared-element transitions between them. + */ +class SmartspaceState() : Parcelable { + var boundsOnScreen: Rect = Rect() + var selectedPage = 0 + + constructor(parcel: Parcel) : this() { + this.boundsOnScreen = parcel.readParcelable(Rect::javaClass.javaClass.classLoader) + this.selectedPage = parcel.readInt() + } + + override fun writeToParcel(dest: Parcel?, flags: Int) { + dest?.writeParcelable(boundsOnScreen, 0) + dest?.writeInt(selectedPage) + } + + override fun describeContents(): Int { + return 0 + } + + override fun toString(): String { + return "boundsOnScreen: $boundsOnScreen, selectedPage: $selectedPage" + } + + companion object CREATOR : Parcelable.Creator<SmartspaceState> { + override fun createFromParcel(parcel: Parcel): SmartspaceState { + return SmartspaceState(parcel) + } + + override fun newArray(size: Int): Array<SmartspaceState?> { + return arrayOfNulls(size) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index aa7f9a24b54a..28534d380c1f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -32,8 +32,10 @@ import com.android.keyguard.clock.ClockManager; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; @@ -44,7 +46,9 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.ViewController; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; import java.util.TimeZone; import javax.inject.Inject; @@ -92,6 +96,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS // If set, will replace keyguard_status_area private View mSmartspaceView; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private SmartspaceTransitionController mSmartspaceTransitionController; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -104,7 +111,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS BatteryController batteryController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController bypassController, - LockscreenSmartspaceController smartspaceController) { + LockscreenSmartspaceController smartspaceController, + KeyguardUnlockAnimationController keyguardUnlockAnimationController, + SmartspaceTransitionController smartspaceTransitionController) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; @@ -116,6 +125,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardUpdateMonitor = keyguardUpdateMonitor; mBypassController = bypassController; mSmartspaceController = smartspaceController; + + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mSmartspaceTransitionController = smartspaceTransitionController; } /** @@ -187,6 +199,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS nic.setLayoutParams(lp); mView.setSmartspaceView(mSmartspaceView); + mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView); } } @@ -285,12 +298,44 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mSmartspaceView != null) { PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X, x, props, animate); + + // If we're unlocking with the SmartSpace shared element transition, let the controller + // know that it should re-position our SmartSpace. + if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { + mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition(); + } else { + // Otherwise, reset Y translation in case it's still offset from a previous shared + // element transition. + ((View) mSmartspaceView).setTranslationY(0f); + } } mKeyguardSliceViewController.updatePosition(x, props, animate); mNotificationIconAreaController.updatePosition(x, props, animate); } + /** Sets an alpha value on every child view except for the smartspace. */ + public void setChildrenAlphaExcludingSmartspace(float alpha) { + final Set<View> excludedViews = new HashSet<>(); + + if (mSmartspaceView != null) { + excludedViews.add(mSmartspaceView); + } + + setChildrenAlphaExcluding(alpha, excludedViews); + } + + /** Sets an alpha value on every child view except for the views in the provided set. */ + public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) { + for (int i = 0; i < mView.getChildCount(); i++) { + final View child = mView.getChildAt(i); + + if (!excludedViews.contains(child)) { + child.setAlpha(alpha); + } + } + } + void updateTimeZone(TimeZone timeZone) { mView.onTimeZoneChanged(timeZone); if (mClockViewController != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 299ecebd021c..96eda3d95603 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -28,6 +28,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; +import android.view.ViewGroup; import android.widget.GridLayout; import android.widget.TextView; @@ -39,6 +40,7 @@ import com.android.systemui.statusbar.CrossFadeHelper; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Set; /** * View consisting of: @@ -55,6 +57,7 @@ public class KeyguardStatusView extends GridLayout { private final LockPatternUtils mLockPatternUtils; private final IActivityManager mIActivityManager; + private ViewGroup mStatusViewContainer; private TextView mLogoutView; private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; @@ -66,6 +69,7 @@ public class KeyguardStatusView extends GridLayout { private float mDarkAmount = 0; private int mTextColor; + private float mChildrenAlphaExcludingSmartSpace = 1f; /** * Bottom margin that defines the margin between bottom of smart space and top of notification @@ -132,6 +136,7 @@ public class KeyguardStatusView extends GridLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); + mStatusViewContainer = findViewById(R.id.status_view_container); mLogoutView = findViewById(R.id.logout); if (mLogoutView != null) { mLogoutView.setOnClickListener(this::onLogoutClicked); @@ -249,6 +254,27 @@ public class KeyguardStatusView extends GridLayout { mClockView.setTextColor(blendedTextColor); } + public void setChildrenAlphaExcludingClockView(float alpha) { + setChildrenAlphaExcluding(alpha, Set.of(mClockView)); + } + + /** Sets an alpha value on every view except for the views in the provided set. */ + public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) { + mChildrenAlphaExcludingSmartSpace = alpha; + + for (int i = 0; i < mStatusViewContainer.getChildCount(); i++) { + final View child = mStatusViewContainer.getChildAt(i); + + if (!excludedViews.contains(child)) { + child.setAlpha(alpha); + } + } + } + + public float getChildrenAlphaExcludingSmartSpace() { + return mChildrenAlphaExcludingSmartSpace; + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardStatusView:"); pw.println(" mOwnerInfo: " + (mOwnerInfo == null diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 388c085fd1a0..7b6514a63195 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -20,6 +20,8 @@ import android.graphics.Rect; import android.os.UserHandle; import android.util.Slog; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -49,6 +51,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final ConfigurationController mConfigurationController; private final DozeParameters mDozeParameters; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private final KeyguardStateController mKeyguardStateController; + private SmartspaceTransitionController mSmartspaceTransitionController; private final Rect mClipBounds = new Rect(); @Inject @@ -59,15 +64,33 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, - DozeParameters dozeParameters) { + DozeParameters dozeParameters, + KeyguardUnlockAnimationController keyguardUnlockAnimationController, + SmartspaceTransitionController smartspaceTransitionController) { super(keyguardStatusView); mKeyguardSliceViewController = keyguardSliceViewController; mKeyguardClockSwitchController = keyguardClockSwitchController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mConfigurationController = configurationController; mDozeParameters = dozeParameters; + mKeyguardStateController = keyguardStateController; mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters); + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mSmartspaceTransitionController = smartspaceTransitionController; + + mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + // If we explicitly re-show the keyguard, make sure that all the child views are + // visible. They might have been animating out as part of the SmartSpace shared + // element transition. + if (keyguardStateController.isShowing()) { + mView.setChildrenAlphaExcludingClockView(1f); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f); + } + } + }); } @Override @@ -137,7 +160,24 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV */ public void setAlpha(float alpha) { if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { - mView.setAlpha(alpha); + // If we're capable of performing the SmartSpace shared element transition, and we are + // going to (we're swiping to dismiss vs. bringing up the PIN screen), then fade out + // everything except for the SmartSpace. + if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { + mView.setChildrenAlphaExcludingClockView(alpha); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(alpha); + } else if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { + // Otherwise, we can just set the alpha for the entire container. + mView.setAlpha(alpha); + + // If we previously unlocked with the shared element transition, some child views + // might still have alpha = 0f. Set them back to 1f since we're just using the + // parent container's alpha. + if (mView.getChildrenAlphaExcludingSmartSpace() < 1f) { + mView.setChildrenAlphaExcludingClockView(1f); + mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f); + } + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 096597afa889..0d31906d2f80 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -16,22 +16,36 @@ package com.android.keyguard; import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; +import android.graphics.drawable.VectorDrawable; import android.util.AttributeSet; import android.view.MotionEvent; +import androidx.annotation.Nullable; + +import com.android.settingslib.Utils; +import com.android.systemui.R; + /** * Similar to the {@link NumPadKey}, but displays an image. */ public class NumPadButton extends AlphaOptimizedImageButton { + @Nullable private NumPadAnimator mAnimator; public NumPadButton(Context context, AttributeSet attrs) { super(context, attrs); - mAnimator = new NumPadAnimator(context, (RippleDrawable) getBackground(), - attrs.getStyleAttribute()); + Drawable background = getBackground(); + if (background instanceof RippleDrawable) { + mAnimator = new NumPadAnimator(context, (RippleDrawable) getBackground(), + attrs.getStyleAttribute()); + } else { + mAnimator = null; + } } @Override @@ -41,7 +55,7 @@ public class NumPadButton extends AlphaOptimizedImageButton { // Set width/height to the same value to ensure a smooth circle for the bg, but shrink // the height to match the old pin bouncer int width = getMeasuredWidth(); - int height = width; + int height = mAnimator == null ? (int) (width * .75f) : width; setMeasuredDimension(getMeasuredWidth(), height); } @@ -50,12 +64,12 @@ public class NumPadButton extends AlphaOptimizedImageButton { protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); - mAnimator.onLayout(b - t); + if (mAnimator != null) mAnimator.onLayout(b - t); } @Override public boolean onTouchEvent(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mAnimator != null) { mAnimator.start(); } return super.onTouchEvent(event); @@ -65,6 +79,13 @@ public class NumPadButton extends AlphaOptimizedImageButton { * Reload colors from resources. **/ public void reloadColors() { - mAnimator.reloadColors(getContext()); + if (mAnimator != null) { + mAnimator.reloadColors(getContext()); + } else { + // Needed for old style pin + int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) + .getDefaultColor(); + ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor)); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 35ace0d1a404..cffa630c6ec8 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -18,6 +18,7 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.os.PowerManager; import android.os.SystemClock; @@ -30,6 +31,8 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityManager; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -47,6 +50,7 @@ public class NumPadKey extends ViewGroup { private int mTextViewResId; private PasswordTextView mTextView; + @Nullable private NumPadAnimator mAnimator; private View.OnClickListener mListener = new View.OnClickListener() { @@ -126,8 +130,13 @@ public class NumPadKey extends ViewGroup { setContentDescription(mDigitText.getText().toString()); - mAnimator = new NumPadAnimator(context, (RippleDrawable) getBackground(), - R.style.NumPadKey); + Drawable background = getBackground(); + if (background instanceof RippleDrawable) { + mAnimator = new NumPadAnimator(context, (RippleDrawable) background, + R.style.NumPadKey); + } else { + mAnimator = null; + } } /** @@ -141,14 +150,14 @@ public class NumPadKey extends ViewGroup { mDigitText.setTextColor(textColor); mKlondikeText.setTextColor(klondikeColor); - mAnimator.reloadColors(getContext()); + if (mAnimator != null) mAnimator.reloadColors(getContext()); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { doHapticKeyClick(); - mAnimator.start(); + if (mAnimator != null) mAnimator.start(); } return super.onTouchEvent(event); @@ -162,7 +171,7 @@ public class NumPadKey extends ViewGroup { // Set width/height to the same value to ensure a smooth circle for the bg, but shrink // the height to match the old pin bouncer int width = getMeasuredWidth(); - int height = width; + int height = mAnimator == null ? (int) (width * .75f) : width; setMeasuredDimension(getMeasuredWidth(), height); } @@ -183,7 +192,7 @@ public class NumPadKey extends ViewGroup { left = centerX - mKlondikeText.getMeasuredWidth() / 2; mKlondikeText.layout(left, top, left + mKlondikeText.getMeasuredWidth(), bottom); - mAnimator.onLayout(b - t); + if (mAnimator != null) mAnimator.onLayout(b - t); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index afda2a406e00..e9c565377530 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -236,7 +236,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { Log.i(TAG, "ScreenDecorations is disabled"); return; } - mHandler = mThreadFactory.builderHandlerOnNewThread("ScreenDecorations"); + mHandler = mThreadFactory.buildHandlerOnNewThread("ScreenDecorations"); mExecutor = mThreadFactory.buildDelayableExecutorOnHandler(mHandler); mExecutor.execute(this::startOnScreenDecorationsThread); mDotViewController.setUiExecutor(mExecutor); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index cc167b928c3c..e6fe0600b9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -133,6 +133,7 @@ public class SystemUIFactory { .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(Transitions.createEmptyForTesting()) .setStartingSurface(Optional.ofNullable(null)); + } mSysUIComponent = builder.build(); if (mInitializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index ee69e277cc46..dba530edc27f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -40,6 +40,7 @@ public class FalsingManagerFake implements FalsingManager { private boolean mIsFalseRobustTap; private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); + private final List<FalsingTapListener> mTapListeners = new ArrayList<>(); @Override public void onSuccessfulUnlock() { @@ -148,11 +149,15 @@ public class FalsingManagerFake implements FalsingManager { @Override public void addTapListener(FalsingTapListener falsingTapListener) { - + mTapListeners.add(falsingTapListener); } @Override public void removeTapListener(FalsingTapListener falsingTapListener) { + mTapListeners.remove(falsingTapListener); + } + public List<FalsingTapListener> getTapListeners() { + return mTapListeners; } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index 26db33d6dea8..053d75d96720 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -46,6 +46,7 @@ import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.media.AudioManager; +import android.media.IAudioService; import android.media.MediaRouter2Manager; import android.media.session.MediaSessionManager; import android.net.ConnectivityManager; @@ -77,6 +78,8 @@ import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.PackageManagerWrapper; +import java.util.Optional; + import javax.inject.Singleton; import dagger.Module; @@ -167,6 +170,13 @@ public class FrameworkServicesModule { @Provides @Singleton + static IAudioService provideIAudioService() { + return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE)); + } + + + @Provides + @Singleton static IBatteryStats provideIBatteryStats() { return IBatteryStats.Stub.asInterface( ServiceManager.getService(BatteryStats.SERVICE_NAME)); @@ -362,6 +372,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static Optional<Vibrator> provideOptionalVibrator(Context context) { + return Optional.ofNullable(context.getSystemService(Vibrator.class)); + } + + @Provides + @Singleton static ViewConfiguration provideViewConfiguration(Context context) { return ViewConfiguration.get(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 1396099db948..f422e9e40d33 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -45,6 +45,7 @@ import com.android.systemui.power.dagger.PowerModule; import com.android.systemui.recents.Recents; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.settings.dagger.SettingsModule; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -173,6 +174,12 @@ public abstract class SystemUIModule { return SystemUIFactory.getInstance(); } + @SysUISingleton + @Provides + static SmartspaceTransitionController provideSmartspaceTransitionController() { + return new SmartspaceTransitionController(); + } + // TODO: This should provided by the WM component /** Provides Optional of BubbleManager */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 411c328cd310..665376ac4569 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -23,11 +23,14 @@ import android.content.Context import android.graphics.Matrix import android.view.RemoteAnimationTarget import android.view.SyncRtSurfaceTransactionApplier +import android.view.View import androidx.core.math.MathUtils import com.android.internal.R +import com.android.keyguard.KeyguardClockSwitchController import com.android.keyguard.KeyguardViewController import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy import javax.inject.Inject @@ -85,7 +88,8 @@ class KeyguardUnlockAnimationController @Inject constructor( context: Context, private val keyguardStateController: KeyguardStateController, private val keyguardViewMediator: Lazy<KeyguardViewMediator>, - private val keyguardViewController: KeyguardViewController + private val keyguardViewController: KeyguardViewController, + private val smartspaceTransitionController: SmartspaceTransitionController ) : KeyguardStateController.Callback { /** @@ -131,6 +135,21 @@ class KeyguardUnlockAnimationController @Inject constructor( /** Rounded corner radius to apply to the surface behind the keyguard. */ private var roundedCornerRadius = 0f + /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */ + public var lockscreenSmartSpace: View? = null + + /** + * Whether we are currently in the process of unlocking the keyguard, and we are performing the + * shared element SmartSpace transition. + */ + private var unlockingWithSmartSpaceTransition: Boolean = false + + /** + * Whether we tried to start the SmartSpace shared element transition for this unlock swipe. + * It's possible we're unable to do so (if the Launcher SmartSpace is not available). + */ + private var attemptedSmartSpaceTransitionForThisSwipe = false + init { surfaceBehindAlphaAnimator.duration = 150 surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN @@ -214,6 +233,24 @@ class KeyguardUnlockAnimationController @Inject constructor( } /** + * Whether we are currently in the process of unlocking the keyguard, and we are performing the + * shared element SmartSpace transition. + */ + fun isUnlockingWithSmartSpaceTransition(): Boolean { + return unlockingWithSmartSpaceTransition + } + + /** + * Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As + * the dismiss amount increases, we will increase our SmartSpace's progress to the destination + * bounds (the location of the Launcher SmartSpace). + */ + fun updateLockscreenSmartSpacePosition() { + smartspaceTransitionController.setProgressToDestinationBounds( + keyguardStateController.dismissAmount / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD) + } + + /** * Scales in and translates up the surface behind the keyguard. This is used during unlock * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is * cancelled). @@ -284,36 +321,85 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + if (keyguardViewController.isShowing) { + updateKeyguardViewMediatorIfThresholdsReached() + + // If the surface is visible or it's about to be, start updating its appearance to + // reflect the new dismiss amount. + if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || + keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { + updateSurfaceBehindAppearAmount() + } + } + + // The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell + // Launcher's SmartSpace to become visible again), so update it even if the keyguard view is + // no longer showing. + updateSmartSpaceTransition() + } + + /** + * Lets the KeyguardViewMediator know if the dismiss amount has crossed a threshold of interest, + * such as reaching the point in the dismiss swipe where we need to make the surface behind the + * keyguard visible. + */ + private fun updateKeyguardViewMediatorIfThresholdsReached() { val dismissAmount = keyguardStateController.dismissAmount // Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have // crossed the threshold to finish the dismissal. val reachedHideKeyguardThreshold = (dismissAmount >= 1f || (keyguardStateController.isDismissingFromSwipe && - // Don't hide if we're flinging during a swipe, since we need to finish - // animating it out. This will be called again after the fling ends. - !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && - dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) + // Don't hide if we're flinging during a swipe, since we need to finish + // animating it out. This will be called again after the fling ends. + !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && + dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { - // We passed the threshold, and we're not yet showing the surface behind the keyguard. - // Animate it in. + // We passed the threshold, and we're not yet showing the surface behind the + // keyguard. Animate it in. keyguardViewMediator.get().showSurfaceBehindKeyguard() fadeInSurfaceBehind() } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD && keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) { - // We're no longer past the threshold but we are showing the surface. Animate it out. + // We're no longer past the threshold but we are showing the surface. Animate it + // out. keyguardViewMediator.get().hideSurfaceBehindKeyguard() fadeOutSurfaceBehind() - } else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe && + } else if (keyguardViewMediator.get() + .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe && reachedHideKeyguardThreshold) { keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished() } + } - if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || - keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { - updateSurfaceBehindAppearAmount() + /** + * Updates flags related to the SmartSpace transition in response to a change in keyguard + * dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher + * know if it needs to do something as a result. + */ + private fun updateSmartSpaceTransition() { + val dismissAmount = keyguardStateController.dismissAmount + + // If we've begun a swipe, and are capable of doing the SmartSpace transition, start it! + if (!attemptedSmartSpaceTransitionForThisSwipe && + dismissAmount > 0f && + dismissAmount < 1f && + keyguardViewController.isShowing) { + attemptedSmartSpaceTransitionForThisSwipe = true + + smartspaceTransitionController.prepareForUnlockTransition() + if (keyguardStateController.canPerformSmartSpaceTransition()) { + unlockingWithSmartSpaceTransition = true + smartspaceTransitionController.launcherSmartspace?.setVisibility( + View.INVISIBLE) + } + } else if (attemptedSmartSpaceTransitionForThisSwipe && + (dismissAmount == 0f || dismissAmount == 1f)) { + attemptedSmartSpaceTransitionForThisSwipe = false + unlockingWithSmartSpaceTransition = false + smartspaceTransitionController.launcherSmartspace?.setVisibility(View.VISIBLE) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 73dfe5e68d9a..075bc700cfa0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -26,21 +26,23 @@ import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay +import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.CrossFadeHelper import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView import javax.inject.Inject -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager /** * Similarly to isShown but also excludes views that have 0 alpha @@ -80,6 +82,7 @@ class MediaHierarchyManager @Inject constructor( wakefulnessLifecycle: WakefulnessLifecycle, private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager ) { + /** * The root overlay of the hierarchy. This is where the media notification is attached to * whenever the view is transitioning from one host to another. It also make sure that the @@ -90,6 +93,30 @@ class MediaHierarchyManager @Inject constructor( private var rootView: View? = null private var currentBounds = Rect() private var animationStartBounds: Rect = Rect() + + /** + * The cross fade progress at the start of the animation. 0.5f means it's just switching between + * the start and the end location and the content is fully faded, while 0.75f means that we're + * halfway faded in again in the target state. + */ + private var animationStartCrossFadeProgress = 0.0f + + /** + * The starting alpha of the animation + */ + private var animationStartAlpha = 0.0f + + /** + * The starting location of the cross fade if an animation is running right now. + */ + @MediaLocation + private var crossFadeAnimationStartLocation = -1 + + /** + * The end location of the cross fade if an animation is running right now. + */ + @MediaLocation + private var crossFadeAnimationEndLocation = -1 private var targetBounds: Rect = Rect() private val mediaFrame get() = mediaCarouselController.mediaFrame @@ -98,9 +125,22 @@ class MediaHierarchyManager @Inject constructor( interpolator = Interpolators.FAST_OUT_SLOW_IN addUpdateListener { updateTargetState() - interpolateBounds(animationStartBounds, targetBounds, animatedFraction, + val currentAlpha: Float + var boundsProgress = animatedFraction + if (isCrossFadeAnimatorRunning) { + animationCrossFadeProgress = MathUtils.lerp(animationStartCrossFadeProgress, 1.0f, + animatedFraction) + // When crossfading, let's keep the bounds at the right location during fading + boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f + currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress, + instantlyShowAtEnd = false) + } else { + // If we're not crossfading, let's interpolate from the start alpha to 1.0f + currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction) + } + interpolateBounds(animationStartBounds, targetBounds, boundsProgress, result = currentBounds) - applyState(currentBounds) + applyState(currentBounds, currentAlpha) } addListener(object : AnimatorListenerAdapter() { private var cancelled: Boolean = false @@ -112,6 +152,7 @@ class MediaHierarchyManager @Inject constructor( } override fun onAnimationEnd(animation: Animator?) { + isCrossFadeAnimatorRunning = false if (!cancelled) { applyTargetStateIfNotAnimating() } @@ -192,11 +233,6 @@ class MediaHierarchyManager @Inject constructor( private var distanceForFullShadeTransition = 0 /** - * Delay after which the media will start transitioning to the full shade on the lockscreen. - */ - private var fullShadeTransitionDelay = 0 - - /** * The amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full * shade. @@ -207,18 +243,33 @@ class MediaHierarchyManager @Inject constructor( return } field = value - if (bypassController.bypassEnabled) { + if (bypassController.bypassEnabled || statusbarState != StatusBarState.KEYGUARD) { + // No need to do all the calculations / updates below if we're not on the lockscreen + // or if we're bypassing. return } - updateDesiredLocation() + updateDesiredLocation(forceNoAnimation = isCurrentlyFading()) if (value >= 0) { updateTargetState() + // Setting the alpha directly, as the below call will use it to update the alpha + carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true) applyTargetStateIfNotAnimating() } } + /** + * Is there currently a cross-fade animation running driven by an animator? + */ + private var isCrossFadeAnimatorRunning = false + + /** + * Are we currently transitionioning from the lockscreen to the full shade + * [StatusBarState.SHADE_LOCKED] or [StatusBarState.SHADE]. Once the user has dragged down and + * the transition starts, this will no longer return true. + */ private val isTransitioningToFullShade: Boolean - get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled + get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled && + statusbarState == StatusBarState.KEYGUARD /** * Set the amount of pixels we have currently dragged down if we're transitioning to the full @@ -227,14 +278,8 @@ class MediaHierarchyManager @Inject constructor( fun setTransitionToFullShadeAmount(value: Float) { // If we're transitioning starting on the shade_locked, we don't want any delay and rather // have it aligned with the rest of the animation - val delay = if (statusbarState == StatusBarState.KEYGUARD) { - fullShadeTransitionDelay - } else { - 0 - } - val progress = MathUtils.saturate((value - delay) / - (distanceForFullShadeTransition - delay)) - fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress) + val progress = MathUtils.saturate(value / distanceForFullShadeTransition) + fullShadeTransitionProgress = progress } /** @@ -296,6 +341,49 @@ class MediaHierarchyManager @Inject constructor( } } + /** + * The current cross fade progress. 0.5f means it's just switching + * between the start and the end location and the content is fully faded, while 0.75f means + * that we're halfway faded in again in the target state. + * This is only valid while [isCrossFadeAnimatorRunning] is true. + */ + private var animationCrossFadeProgress = 1.0f + + /** + * The current carousel Alpha. + */ + private var carouselAlpha: Float = 1.0f + set(value) { + if (field == value) { + return + } + field = value + CrossFadeHelper.fadeIn(mediaFrame, value) + } + + /** + * Calculate the alpha of the view when given a cross-fade progress. + * + * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching + * between the start and the end location and the content is fully faded, while 0.75f means + * that we're halfway faded in again in the target state. + * + * @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed + * to avoid fadinging in when the target was hidden anyway. + */ + private fun calculateAlphaFromCrossFade( + crossFadeProgress: Float, + instantlyShowAtEnd: Boolean + ): Float { + if (crossFadeProgress <= 0.5f) { + return 1.0f - crossFadeProgress / 0.5f + } else if (instantlyShowAtEnd) { + return 1.0f + } else { + return (crossFadeProgress - 0.5f) / 0.5f + } + } + init { updateConfiguration() configurationController.addCallback(object : ConfigurationController.ConfigurationListener { @@ -375,9 +463,7 @@ class MediaHierarchyManager @Inject constructor( private fun updateConfiguration() { distanceForFullShadeTransition = context.resources.getDimensionPixelSize( - R.dimen.lockscreen_shade_qs_transition_distance) - fullShadeTransitionDelay = context.resources.getDimensionPixelSize( - R.dimen.lockscreen_shade_media_transition_start_delay) + R.dimen.lockscreen_shade_media_transition_distance) } /** @@ -449,8 +535,13 @@ class MediaHierarchyManager @Inject constructor( shouldAnimateTransition(desiredLocation, previousLocation) val (animDuration, delay) = getAnimationParams(previousLocation, desiredLocation) val host = getHost(desiredLocation) - mediaCarouselController.onDesiredLocationChanged(desiredLocation, host, animate, - animDuration, delay) + val willFade = calculateTransformationType() == TRANSFORMATION_TYPE_FADE + if (!willFade || isCurrentlyInGuidedTransformation() || !animate) { + // if we're fading, we want the desired location / measurement only to change + // once fully faded. This is happening in the host attachment + mediaCarouselController.onDesiredLocationChanged(desiredLocation, host, + animate, animDuration, delay) + } performTransitionToNewLocation(isNewView, animate) } } @@ -470,6 +561,8 @@ class MediaHierarchyManager @Inject constructor( if (isCurrentlyInGuidedTransformation()) { applyTargetStateIfNotAnimating() } else if (animate) { + val wasCrossFading = isCrossFadeAnimatorRunning + val previewsCrossFadeProgress = animationCrossFadeProgress animator.cancel() if (currentAttachmentLocation != previousLocation || !previousHost.hostView.isAttachedToWindow) { @@ -482,6 +575,42 @@ class MediaHierarchyManager @Inject constructor( // be outdated animationStartBounds.set(previousHost.currentBounds) } + val transformationType = calculateTransformationType() + var needsCrossFade = transformationType == TRANSFORMATION_TYPE_FADE + var crossFadeStartProgress = 0.0f + // The alpha is only relevant when not cross fading + var newCrossFadeStartLocation = previousLocation + if (wasCrossFading) { + if (currentAttachmentLocation == crossFadeAnimationEndLocation) { + if (needsCrossFade) { + // We were previously crossFading and we've already reached + // the end view, Let's start crossfading from the same position there + crossFadeStartProgress = 1.0f - previewsCrossFadeProgress + } + // Otherwise let's fade in from the current alpha, but not cross fade + } else { + // We haven't reached the previous location yet, let's still cross fade from + // where we were. + newCrossFadeStartLocation = crossFadeAnimationStartLocation + if (newCrossFadeStartLocation == desiredLocation) { + // we're crossFading back to where we were, let's start at the end position + crossFadeStartProgress = 1.0f - previewsCrossFadeProgress + } else { + // Let's start from where we are right now + crossFadeStartProgress = previewsCrossFadeProgress + // We need to force cross fading as we haven't reached the end location yet + needsCrossFade = true + } + } + } else if (needsCrossFade) { + // let's not flicker and start with the same alpha + crossFadeStartProgress = (1.0f - carouselAlpha) / 2.0f + } + isCrossFadeAnimatorRunning = needsCrossFade + crossFadeAnimationStartLocation = newCrossFadeStartLocation + crossFadeAnimationEndLocation = desiredLocation + animationStartAlpha = carouselAlpha + animationStartCrossFadeProgress = crossFadeStartProgress adjustAnimatorForTransition(desiredLocation, previousLocation) if (!animationPending) { rootView?.let { @@ -518,6 +647,17 @@ class MediaHierarchyManager @Inject constructor( // non-trivial reattaching logic happening that will make the view not-shown earlier return true } + + if (statusbarState == StatusBarState.KEYGUARD) { + if (currentLocation == LOCATION_LOCKSCREEN && + previousLocation == LOCATION_QS || + (currentLocation == LOCATION_QS && + previousLocation == LOCATION_LOCKSCREEN)) { + // We're always fading from lockscreen to keyguard in situations where the player + // is already fully hidden + return false + } + } return mediaFrame.isShownNotFaded || animator.isRunning || animationPending } @@ -538,7 +678,7 @@ class MediaHierarchyManager @Inject constructor( keyguardStateController.isKeyguardFadingAway) { delay = keyguardStateController.keyguardFadingAwayDelay } - animDuration = StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE.toLong() + animDuration = (StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE / 2f).toLong() } else if (previousLocation == LOCATION_QQS && desiredLocation == LOCATION_LOCKSCREEN) { animDuration = StackStateAnimator.ANIMATION_DURATION_APPEAR_DISAPPEAR.toLong() } @@ -550,7 +690,7 @@ class MediaHierarchyManager @Inject constructor( // Let's immediately apply the target state (which is interpolated) if there is // no animation running. Otherwise the animation update will already update // the location - applyState(targetBounds) + applyState(targetBounds, carouselAlpha) } } @@ -558,7 +698,7 @@ class MediaHierarchyManager @Inject constructor( * Updates the bounds that the view wants to be in at the end of the animation. */ private fun updateTargetState() { - if (isCurrentlyInGuidedTransformation()) { + if (isCurrentlyInGuidedTransformation() && !isCurrentlyFading()) { val progress = getTransformationProgress() var endHost = getHost(desiredLocation)!! var starthost = getHost(previousLocation)!! @@ -606,12 +746,33 @@ class MediaHierarchyManager @Inject constructor( } /** + * Calculate the transformation type for the current animation + */ + @VisibleForTesting + @TransformationType + fun calculateTransformationType(): Int { + if (isTransitioningToFullShade) { + return TRANSFORMATION_TYPE_FADE + } + if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS || + previousLocation == LOCATION_QS && desiredLocation == LOCATION_LOCKSCREEN) { + // animating between ls and qs should fade, as QS is clipped. + return TRANSFORMATION_TYPE_FADE + } + if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QQS) { + // animating between ls and qqs should fade when dragging down via e.g. expand button + return TRANSFORMATION_TYPE_FADE + } + return TRANSFORMATION_TYPE_TRANSITION + } + + /** * @return the current transformation progress if we're in a guided transformation and -1 * otherwise */ private fun getTransformationProgress(): Float { val progress = getQSTransformationProgress() - if (progress >= 0) { + if (statusbarState != StatusBarState.KEYGUARD && progress >= 0) { return progress } if (isTransitioningToFullShade) { @@ -643,19 +804,20 @@ class MediaHierarchyManager @Inject constructor( private fun cancelAnimationAndApplyDesiredState() { animator.cancel() getHost(desiredLocation)?.let { - applyState(it.currentBounds, immediately = true) + applyState(it.currentBounds, alpha = 1.0f, immediately = true) } } /** * Apply the current state to the view, updating it's bounds and desired state */ - private fun applyState(bounds: Rect, immediately: Boolean = false) { + private fun applyState(bounds: Rect, alpha: Float, immediately: Boolean = false) { currentBounds.set(bounds) - val currentlyInGuidedTransformation = isCurrentlyInGuidedTransformation() - val startLocation = if (currentlyInGuidedTransformation) previousLocation else -1 - val progress = if (currentlyInGuidedTransformation) getTransformationProgress() else 1.0f - val endLocation = desiredLocation + carouselAlpha = if (isCurrentlyFading()) alpha else 1.0f + val onlyUseEndState = !isCurrentlyInGuidedTransformation() || isCurrentlyFading() + val startLocation = if (onlyUseEndState) -1 else previousLocation + val progress = if (onlyUseEndState) 1.0f else getTransformationProgress() + val endLocation = resolveLocationForFading() mediaCarouselController.setCurrentState(startLocation, endLocation, progress, immediately) updateHostAttachment() if (currentAttachmentLocation == IN_OVERLAY) { @@ -668,8 +830,19 @@ class MediaHierarchyManager @Inject constructor( } private fun updateHostAttachment() { - val inOverlay = isTransitionRunning() && rootOverlay != null - val newLocation = if (inOverlay) IN_OVERLAY else desiredLocation + var newLocation = resolveLocationForFading() + var canUseOverlay = !isCurrentlyFading() + if (isCrossFadeAnimatorRunning) { + if (getHost(newLocation)?.visible == true && + getHost(newLocation)?.hostView?.isShown == false && + newLocation != desiredLocation) { + // We're crossfading but the view is already hidden. Let's move to the overlay + // instead. This happens when animating to the full shade using a button click. + canUseOverlay = true + } + } + val inOverlay = isTransitionRunning() && rootOverlay != null && canUseOverlay + newLocation = if (inOverlay) IN_OVERLAY else newLocation if (currentAttachmentLocation != newLocation) { currentAttachmentLocation = newLocation @@ -677,10 +850,10 @@ class MediaHierarchyManager @Inject constructor( (mediaFrame.parent as ViewGroup?)?.removeView(mediaFrame) // Add it to the new one - val targetHost = getHost(desiredLocation)!!.hostView if (inOverlay) { rootOverlay!!.add(mediaFrame) } else { + val targetHost = getHost(newLocation)!!.hostView // When adding back to the host, let's make sure to reset the bounds. // Usually adding the view will trigger a layout that does this automatically, // but we sometimes suppress this. @@ -693,7 +866,37 @@ class MediaHierarchyManager @Inject constructor( left + currentBounds.width(), top + currentBounds.height()) } + if (isCrossFadeAnimatorRunning) { + // When cross-fading with an animation, we only notify the media carousel of the + // location change, once the view is reattached to the new place and not immediately + // when the desired location changes. This callback will update the measurement + // of the carousel, only once we've faded out at the old location and then reattach + // to fade it in at the new location. + mediaCarouselController.onDesiredLocationChanged( + newLocation, + getHost(newLocation), + animate = false + ) + } + } + } + + /** + * Calculate the location when cross fading between locations. While fading out, + * the content should remain in the previous location, while after the switch it should + * be at the desired location. + */ + private fun resolveLocationForFading(): Int { + if (isCrossFadeAnimatorRunning) { + // When animating between two hosts with a fade, let's keep ourselves in the old + // location for the first half, and then switch over to the end location + if (animationCrossFadeProgress > 0.5 || previousLocation == -1) { + return crossFadeAnimationEndLocation + } else { + return crossFadeAnimationStartLocation + } } + return desiredLocation } private fun isTransitionRunning(): Boolean { @@ -708,29 +911,29 @@ class MediaHierarchyManager @Inject constructor( return desiredLocation } val onLockscreen = (!bypassController.bypassEnabled && - (statusbarState == StatusBarState.KEYGUARD || - statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) + (statusbarState == StatusBarState.KEYGUARD || + statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER)) val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications() val location = when { qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS - onLockscreen && isTransitioningToFullShade -> LOCATION_QQS + onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS } // When we're on lock screen and the player is not active, we should keep it in QS. // Otherwise it will try to animate a transition that doesn't make sense. if (location == LOCATION_LOCKSCREEN && getHost(location)?.visible != true && - !statusBarStateController.isDozing) { + !statusBarStateController.isDozing) { return LOCATION_QS } if (location == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS && - collapsingShadeFromQS) { + collapsingShadeFromQS) { // When collapsing on the lockscreen, we want to remain in QS return LOCATION_QS } if (location != LOCATION_LOCKSCREEN && desiredLocation == LOCATION_LOCKSCREEN && - !fullyAwake) { + !fullyAwake) { // When unlocking from dozing / while waking up, the media shouldn't be transitioning // in an animated way. Let's keep it in the lockscreen until we're fully awake and // reattach it without an animation @@ -740,6 +943,26 @@ class MediaHierarchyManager @Inject constructor( } /** + * Are we currently transforming to the full shade and already in QQS + */ + private fun isTransformingToFullShadeAndInQQS(): Boolean { + if (!isTransitioningToFullShade) { + return false + } + return fullShadeTransitionProgress > 0.5f + } + + /** + * Is the current transformationType fading + */ + private fun isCurrentlyFading(): Boolean { + if (isTransitioningToFullShade) { + return true + } + return isCrossFadeAnimatorRunning + } + + /** * Returns true when the media card could be visible to the user if existed. */ private fun isVisibleToUser(): Boolean { @@ -789,9 +1012,27 @@ class MediaHierarchyManager @Inject constructor( * Attached at the root of the hierarchy in an overlay */ const val IN_OVERLAY = -1000 + + /** + * The default transformation type where the hosts transform into each other using a direct + * transition + */ + const val TRANSFORMATION_TYPE_TRANSITION = 0 + + /** + * A transformation type where content fades from one place to another instead of + * transitioning + */ + const val TRANSFORMATION_TYPE_FADE = 1 } } +@IntDef(prefix = ["TRANSFORMATION_TYPE_"], value = [ + MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION, + MediaHierarchyManager.TRANSFORMATION_TYPE_FADE]) +@Retention(AnnotationRetention.SOURCE) +private annotation class TransformationType + @IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS, MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN]) @Retention(AnnotationRetention.SOURCE) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index b0be57668d08..3bf7fb07f8eb 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -23,7 +23,6 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.media.MediaDescription -import android.media.session.MediaController import android.os.UserHandle import android.provider.Settings import android.service.media.MediaBrowserService @@ -171,6 +170,7 @@ class MediaResumeListener @Inject constructor( if (useMediaResumption) { // If this had been started from a resume state, disconnect now that it's live mediaBrowser?.disconnect() + mediaBrowser = null // If we don't have a resume action, check if we haven't already if (data.resumeAction == null && !data.hasCheckedForResume && !blockedApps.contains(data.packageName) && data.isLocalSession) { @@ -201,7 +201,6 @@ class MediaResumeListener @Inject constructor( */ private fun tryUpdateResumptionList(key: String, componentName: ComponentName) { Log.d(TAG, "Testing if we can connect to $componentName") - mediaBrowser?.disconnect() mediaBrowser = mediaBrowserFactory.create( object : ResumeMediaBrowser.Callback() { override fun onConnected() { @@ -211,7 +210,6 @@ class MediaResumeListener @Inject constructor( override fun onError() { Log.e(TAG, "Cannot resume with $componentName") mediaDataManager.setResumeAction(key, null) - mediaBrowser?.disconnect() mediaBrowser = null } @@ -224,7 +222,6 @@ class MediaResumeListener @Inject constructor( Log.d(TAG, "Can get resumable media from $componentName") mediaDataManager.setResumeAction(key, getResumeAction(componentName)) updateResumptionList(componentName) - mediaBrowser?.disconnect() mediaBrowser = null } }, @@ -262,30 +259,7 @@ class MediaResumeListener @Inject constructor( */ private fun getResumeAction(componentName: ComponentName): Runnable { return Runnable { - mediaBrowser?.disconnect() - mediaBrowser = mediaBrowserFactory.create( - object : ResumeMediaBrowser.Callback() { - override fun onConnected() { - if (mediaBrowser?.token == null) { - Log.e(TAG, "Error after connect") - mediaBrowser?.disconnect() - mediaBrowser = null - return - } - Log.d(TAG, "Connected for restart $componentName") - val controller = MediaController(context, mediaBrowser!!.token) - val controls = controller.transportControls - controls.prepare() - controls.play() - } - - override fun onError() { - Log.e(TAG, "Resume failed for $componentName") - mediaBrowser?.disconnect() - mediaBrowser = null - } - }, - componentName) + mediaBrowser = mediaBrowserFactory.create(null, componentName) mediaBrowser?.restart() } } diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java index e453653cf7b5..fecc903326f5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java @@ -16,6 +16,7 @@ package com.android.systemui.media; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -47,7 +48,7 @@ public class ResumeMediaBrowser { private static final String TAG = "ResumeMediaBrowser"; private final Context mContext; - private final Callback mCallback; + @Nullable private final Callback mCallback; private MediaBrowserFactory mBrowserFactory; private MediaBrowser mMediaBrowser; private ComponentName mComponentName; @@ -58,8 +59,8 @@ public class ResumeMediaBrowser { * @param callback used to report media items found * @param componentName Component name of the MediaBrowserService this browser will connect to */ - public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName, - MediaBrowserFactory browserFactory) { + public ResumeMediaBrowser(Context context, @Nullable Callback callback, + ComponentName componentName, MediaBrowserFactory browserFactory) { mContext = context; mCallback = callback; mComponentName = componentName; @@ -93,18 +94,24 @@ public class ResumeMediaBrowser { List<MediaBrowser.MediaItem> children) { if (children.size() == 0) { Log.d(TAG, "No children found for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } } else { // We ask apps to return a playable item as the first child when sending // a request with EXTRA_RECENT; if they don't, no resume controls MediaBrowser.MediaItem child = children.get(0); MediaDescription desc = child.getDescription(); if (child.isPlayable() && mMediaBrowser != null) { - mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), - ResumeMediaBrowser.this); + if (mCallback != null) { + mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(), + ResumeMediaBrowser.this); + } } else { Log.d(TAG, "Child found but not playable for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } } } disconnect(); @@ -113,7 +120,9 @@ public class ResumeMediaBrowser { @Override public void onError(String parentId) { Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } @@ -121,7 +130,9 @@ public class ResumeMediaBrowser { public void onError(String parentId, Bundle options) { Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId + ", options: " + options); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } }; @@ -138,13 +149,20 @@ public class ResumeMediaBrowser { Log.d(TAG, "Service connected for " + mComponentName); if (mMediaBrowser != null && mMediaBrowser.isConnected()) { String root = mMediaBrowser.getRoot(); - if (!TextUtils.isEmpty(root) && mMediaBrowser != null) { - mCallback.onConnected(); - mMediaBrowser.subscribe(root, mSubscriptionCallback); + if (!TextUtils.isEmpty(root)) { + if (mCallback != null) { + mCallback.onConnected(); + } + if (mMediaBrowser != null) { + mMediaBrowser.subscribe(root, mSubscriptionCallback); + } return; } } - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } /** @@ -153,7 +171,9 @@ public class ResumeMediaBrowser { @Override public void onConnectionSuspended() { Log.d(TAG, "Connection suspended for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } @@ -163,16 +183,18 @@ public class ResumeMediaBrowser { @Override public void onConnectionFailed() { Log.d(TAG, "Connection failed for " + mComponentName); - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } disconnect(); } }; /** - * Disconnect the media browser. This should be called after restart or testConnection have - * completed to close the connection. + * Disconnect the media browser. This should be done after callbacks have completed to + * disconnect from the media browser service. */ - public void disconnect() { + protected void disconnect() { if (mMediaBrowser != null) { mMediaBrowser.disconnect(); } @@ -183,7 +205,8 @@ public class ResumeMediaBrowser { * Connects to the MediaBrowserService and starts playback. * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called * depending on whether it was successful. - * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed. + * If the connection is successful, the listener should call ResumeMediaBrowser#disconnect after + * getting a media update from the app */ public void restart() { disconnect(); @@ -195,7 +218,10 @@ public class ResumeMediaBrowser { public void onConnected() { Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected()); if (mMediaBrowser == null || !mMediaBrowser.isConnected()) { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); return; } MediaSession.Token token = mMediaBrowser.getSessionToken(); @@ -203,17 +229,26 @@ public class ResumeMediaBrowser { controller.getTransportControls(); controller.getTransportControls().prepare(); controller.getTransportControls().play(); - mCallback.onConnected(); + if (mCallback != null) { + mCallback.onConnected(); + } + // listener should disconnect after media player update } @Override public void onConnectionFailed() { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } @Override public void onConnectionSuspended() { - mCallback.onError(); + if (mCallback != null) { + mCallback.onError(); + } + disconnect(); } }, rootHints); mMediaBrowser.connect(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index ae9b5987bac5..0515fed21e07 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -123,8 +123,7 @@ public class NavigationBarController implements Callbacks, // Tracks config changes that will actually recreate the nav bar private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( - ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_SCREEN_LAYOUT + ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE); @Inject diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 34c654c9135d..1c5fa439d0ee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -95,7 +95,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private boolean mLastKeyguardAndExpanded; /** * The last received state from the controller. This should not be used directly to check if - * we're on keyguard but use {@link #isKeyguardShowing()} instead since that is more accurate + * we're on keyguard but use {@link #isKeyguardState()} instead since that is more accurate * during state transitions which often call into us. */ private int mState; @@ -326,7 +326,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca || mHeaderAnimating; mQSPanelController.setExpanded(mQsExpanded); mQSDetail.setExpanded(mQsExpanded); - boolean keyguardShowing = isKeyguardShowing(); + boolean keyguardShowing = isKeyguardState(); mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating || mShowCollapsedOnKeyguard) ? View.VISIBLE @@ -344,7 +344,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE); } - private boolean isKeyguardShowing() { + private boolean isKeyguardState() { // We want the freshest state here since otherwise we'll have some weirdness if earlier // listeners trigger updates return mStatusBarStateController.getState() == StatusBarState.KEYGUARD; @@ -366,7 +366,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mQSAnimator != null) { mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed); } - if (!showCollapsed && isKeyguardShowing()) { + if (!showCollapsed && isKeyguardState()) { setQsExpansion(mLastQSExpansion, 0); } } @@ -457,7 +457,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mContainer.setExpansion(expansion); final float translationScaleY = (mTranslateWhileExpanding ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); - boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; + boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard; if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( onKeyguardAndExpanded @@ -531,7 +531,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca // The Media can be scrolled off screen by default, let's offset it float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY() + mQSPanelScrollView.getScrollRange(); - // The expanded media host should never move below the laid out position pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */); // The expanded media host should never move above the laid out position pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */); @@ -540,7 +539,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) { View hostView = mediaHost.getHostView(); - if (mLastQSExpansion > 0) { + // on keyguard we cross-fade to expanded, so no need to pin it. + if (mLastQSExpansion > 0 && !isKeyguardState()) { float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView) - hostView.getHeight(); float currentPosition = mediaHost.getCurrentBounds().top @@ -573,7 +573,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private boolean headerWillBeAnimating() { return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard - && !isKeyguardShowing(); + && !isKeyguardState(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index be7adbc77ca1..74d3425edd22 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -430,12 +430,14 @@ public class InternetTile extends QSTileImpl<SignalState> { } else { state.icon = ResourceIcon.get(cb.mWifiSignalIconId); } - } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); - } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } else if (cb.mNoDefaultNetwork) { + if (cb.mNoNetworksAvailable) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } } else if (cb.mIsTransient) { state.icon = ResourceIcon.get( com.android.internal.R.drawable.ic_signal_wifi_transient_animation); @@ -488,12 +490,14 @@ public class InternetTile extends QSTileImpl<SignalState> { state.state = Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); state.secondaryLabel = r.getString(R.string.status_bar_airplane); - } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); - } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) { - state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); - state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } else if (cb.mNoDefaultNetwork) { + if (cb.mNoNetworksAvailable) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable); + } else { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available); + state.secondaryLabel = r.getString(R.string.quick_settings_networks_available); + } } else { state.icon = new SignalIcon(cb.mMobileSignalIconId); state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index e46792555101..64aec5e0b32b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -18,13 +18,14 @@ package com.android.systemui.qs.tiles; import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; + import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.service.quickaccesswallet.GetWalletCardsError; -import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.WalletCard; @@ -51,6 +52,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.systemui.wallet.ui.WalletActivity; import java.util.List; @@ -66,16 +68,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { private final CharSequence mLabel = mContext.getString(R.string.wallet_title); private final WalletCardRetriever mCardRetriever = new WalletCardRetriever(); - // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes. - private final QuickAccessWalletClient mQuickAccessWalletClient; private final KeyguardStateController mKeyguardStateController; private final PackageManager mPackageManager; private final SecureSettings mSecureSettings; private final Executor mExecutor; + private final QuickAccessWalletController mController; private final FeatureFlags mFeatureFlags; - @VisibleForTesting Drawable mCardViewDrawable; private WalletCard mSelectedCard; + @VisibleForTesting Drawable mCardViewDrawable; @Inject public QuickAccessWalletTile( @@ -87,15 +88,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - QuickAccessWalletClient quickAccessWalletClient, KeyguardStateController keyguardStateController, PackageManager packageManager, SecureSettings secureSettings, - @Background Executor executor, + @Main Executor executor, + QuickAccessWalletController quickAccessWalletController, FeatureFlags featureFlags) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); - mQuickAccessWalletClient = quickAccessWalletClient; + mController = quickAccessWalletController; mKeyguardStateController = keyguardStateController; mPackageManager = packageManager; mSecureSettings = secureSettings; @@ -115,7 +116,11 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { protected void handleSetListening(boolean listening) { super.handleSetListening(listening); if (listening) { - queryWalletCards(); + mController.setupWalletChangeObservers(mCardRetriever, DEFAULT_PAYMENT_APP_CHANGE); + if (!mController.getWalletClient().isWalletServiceAvailable()) { + mController.reCreateWalletClient(); + } + mController.queryWalletCards(mCardRetriever); } } @@ -139,12 +144,13 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { mContext.startActivity(intent); } } else { - if (mQuickAccessWalletClient.createWalletIntent() == null) { + if (mController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0, + mController.getWalletClient().createWalletIntent(), + /* delay= */ 0, animationController); } }); @@ -152,30 +158,34 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override protected void handleUpdateState(State state, Object arg) { - CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + CharSequence label = mController.getWalletClient().getServiceLabel(); state.label = label == null ? mLabel : label; state.contentDescription = state.label; state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); - if (mQuickAccessWalletClient.isWalletServiceAvailable()) { + if (mController.getWalletClient().isWalletServiceAvailable()) { if (mSelectedCard != null) { if (isDeviceLocked) { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_device_locked); + state.sideViewCustomDrawable = null; } else { state.state = Tile.STATE_ACTIVE; state.secondaryLabel = mSelectedCard.getContentDescription(); + state.sideViewCustomDrawable = mCardViewDrawable; } } else { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_no_card); + state.sideViewCustomDrawable = null; } state.stateDescription = state.secondaryLabel; } else { state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = null; + state.sideViewCustomDrawable = null; } - state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable; } @Override @@ -198,19 +208,14 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public CharSequence getTileLabel() { - CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + CharSequence label = mController.getWalletClient().getServiceLabel(); return label == null ? mLabel : label; } - private void queryWalletCards() { - int cardWidth = - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); - int cardHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); - int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); - GetWalletCardsRequest request = - new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); - mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever); + @Override + protected void handleDestroy() { + super.handleDestroy(); + mController.unregisterWalletChangeObservers(DEFAULT_PAYMENT_APP_CHANGE); } private class WalletCardRetriever implements diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 89cb5af58928..f4c15fbf90b8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHE import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; +import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; @@ -88,6 +89,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; @@ -148,6 +150,7 @@ public class OverviewProxyService extends CurrentUserTracker implements private final CommandQueue mCommandQueue; private final ShellTransitions mShellTransitions; private final Optional<StartingSurface> mStartingSurface; + private final SmartspaceTransitionController mSmartspaceTransitionController; private Region mActiveNavBarRegion; @@ -552,6 +555,9 @@ public class OverviewProxyService extends CurrentUserTracker implements mStartingSurface.ifPresent((startingwindow) -> params.putBinder( KEY_EXTRA_SHELL_STARTING_WINDOW, startingwindow.createExternalInterface().asBinder())); + params.putBinder( + KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER, + mSmartspaceTransitionController.createExternalInterface().asBinder()); try { Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser=" @@ -613,7 +619,8 @@ public class OverviewProxyService extends CurrentUserTracker implements Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, - Optional<StartingSurface> startingSurface) { + Optional<StartingSurface> startingSurface, + SmartspaceTransitionController smartspaceTransitionController) { super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; @@ -675,6 +682,7 @@ public class OverviewProxyService extends CurrentUserTracker implements updateEnabledState(); startConnectionToCurrentUser(); mStartingSurface = startingSurface; + mSmartspaceTransitionController = smartspaceTransitionController; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index af4836d997fa..facebee52943 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -861,11 +861,15 @@ public class ScreenshotView extends FrameLayout implements class SwipeDismissHandler implements OnTouchListener { // distance needed to register a dismissal - private static final float DISMISS_DISTANCE_THRESHOLD_DP = 30; + private static final float DISMISS_DISTANCE_THRESHOLD_DP = 20; private final GestureDetector mGestureDetector; private float mStartX; + // Keeps track of the most recent direction (between the last two move events). + // -1 for left; +1 for right. + private int mDirectionX; + private float mPreviousX; SwipeDismissHandler() { GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener(); @@ -878,6 +882,7 @@ public class ScreenshotView extends FrameLayout implements mCallbacks.onUserInteraction(); if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mStartX = event.getRawX(); + mPreviousX = mStartX; return true; } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { if (isPastDismissThreshold() @@ -906,16 +911,42 @@ public class ScreenshotView extends FrameLayout implements public boolean onScroll( MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) { mScreenshotStatic.setTranslationX(ev2.getRawX() - mStartX); + mDirectionX = (ev2.getRawX() < mPreviousX) ? -1 : 1; + mPreviousX = ev2.getRawX(); return true; } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + if (mScreenshotStatic.getTranslationX() * velocityX > 0 + && (mDismissAnimation == null || !mDismissAnimation.isRunning())) { + animateDismissal(createSwipeDismissAnimation(velocityX / (float) 1000)); + return true; + } + return false; + } } private boolean isPastDismissThreshold() { - float distance = Math.abs(mScreenshotStatic.getTranslationX()); - return distance >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP); + float translationX = mScreenshotStatic.getTranslationX(); + // Determines whether the absolute translation from the start is in the same direction + // as the current movement. For example, if the user moves most of the way to the right, + // but then starts dragging back left, we do not dismiss even though the absolute + // distance is greater than the threshold. + if (translationX * mDirectionX > 0) { + return Math.abs(translationX) >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP); + } + return false; } private ValueAnimator createSwipeDismissAnimation() { + return createSwipeDismissAnimation(1); + } + + private ValueAnimator createSwipeDismissAnimation(float velocity) { + // velocity is measured in pixels per millisecond + velocity = Math.min(3, Math.max(1, velocity)); ValueAnimator anim = ValueAnimator.ofFloat(0, 1); float startX = mScreenshotStatic.getTranslationX(); // make sure the UI gets all the way off the screen in the direction of movement @@ -924,13 +955,14 @@ public class ScreenshotView extends FrameLayout implements float finalX = startX < 0 ? -1 * mActionsContainerBackground.getRight() : mDisplayMetrics.widthPixels; + float distance = Math.abs(finalX - startX); anim.addUpdateListener(animation -> { float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction()); mScreenshotStatic.setTranslationX(translation); setAlpha(1 - animation.getAnimatedFraction()); }); - anim.setDuration(400); + anim.setDuration((long) (distance / Math.abs(velocity))); return anim; } diff --git a/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt new file mode 100644 index 000000000000..89b3df0f495f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system.smartspace + +import android.graphics.Rect +import android.view.View +import com.android.systemui.shared.system.ActivityManagerWrapper +import com.android.systemui.shared.system.QuickStepContract +import kotlin.math.min + +/** + * Controller that keeps track of SmartSpace instances in remote processes (such as Launcher), + * allowing System UI to query or update their state during shared-element transitions. + */ +class SmartspaceTransitionController { + + /** + * Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it + * to provide us with a callback to query and update the state of its Smartspace. + */ + private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() { + override fun setSmartspace(callback: ISmartspaceCallback?) { + this@SmartspaceTransitionController.launcherSmartspace = callback + updateLauncherSmartSpaceState() + } + } + + /** + * Callback provided by Launcher to allow us to query and update the state of its SmartSpace. + */ + public var launcherSmartspace: ISmartspaceCallback? = null + + public var lockscreenSmartspace: View? = null + + /** + * Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid + * unnecessary + */ + public var mLauncherSmartspaceState: SmartspaceState? = null + + /** + * The bounds of our SmartSpace when the shared element transition began. We'll interpolate + * between this and [smartspaceDestinationBounds] as the dismiss amount changes. + */ + private val smartspaceOriginBounds = Rect() + + /** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */ + + private val smartspaceDestinationBounds = Rect() + + fun createExternalInterface(): ISmartspaceTransitionController { + return ISmartspaceTransitionController + } + + /** + * Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the + * cached [mLauncherSmartspaceState] if possible. + */ + fun updateLauncherSmartSpaceState(): SmartspaceState? { + return launcherSmartspace?.smartspaceState.also { + mLauncherSmartspaceState = it + } + } + + fun prepareForUnlockTransition() { + updateLauncherSmartSpaceState().also { state -> + if (state?.boundsOnScreen != null && lockscreenSmartspace != null) { + lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds) + with(smartspaceDestinationBounds) { + set(state.boundsOnScreen) + offset(-lockscreenSmartspace!!.paddingLeft, + -lockscreenSmartspace!!.paddingTop) + } + } + } + } + + fun setProgressToDestinationBounds(progress: Float) { + if (!isSmartspaceTransitionPossible()) { + return + } + + val progressClamped = min(1f, progress) + + // Calculate the distance (relative to the origin) that we need to be for the current + // progress value. + val progressX = + (smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped + val progressY = + (smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped + + val lockscreenSmartspaceCurrentBounds = Rect().also { + lockscreenSmartspace!!.getBoundsOnScreen(it) + } + + // Figure out how far that is from our present location on the screen. This approach + // compensates for the fact that our parent container is also translating to animate out. + val dx = smartspaceOriginBounds.left + progressX - + lockscreenSmartspaceCurrentBounds.left + var dy = smartspaceOriginBounds.top + progressY - + lockscreenSmartspaceCurrentBounds.top + + with(lockscreenSmartspace!!) { + translationX = translationX + dx + translationY = translationY + dy + } + } + + /** + * Whether we're capable of performing the Smartspace shared element transition when we unlock. + * This is true if: + * + * - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen. + * - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected + * page. + */ + public fun isSmartspaceTransitionPossible(): Boolean { + val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true + return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty + } + + companion object { + fun isLauncherUnderneath(): Boolean { + return ActivityManagerWrapper.getInstance() + .runningTask?.topActivity?.className?.equals( + QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index ac6d9e8820ad..c7b6e67cf96d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -61,10 +61,6 @@ public class FeatureFlags { return mFlagReader.isEnabled(R.bool.flag_conversations); } - public boolean isToastStyleEnabled() { - return mFlagReader.isEnabled(R.bool.flag_toast_style); - } - public boolean isMonetEnabled() { return mFlagReader.isEnabled(R.bool.flag_monet); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index b33424c563a3..acfd998ff6e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -35,7 +35,6 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener; -import java.util.List; /** * The view in the statusBar that contains part of the heads-up information @@ -55,10 +54,8 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { private int[] mTmpPosition = new int[2]; private boolean mFirstLayout = true; private int mMaxWidth; - private View mRootView; private int mSysWinInset; private int mCutOutInset; - private List<Rect> mCutOutBounds; private Rect mIconDrawingRect = new Rect(); private Point mDisplaySize; private Runnable mOnDrawingRectChangedListener; @@ -197,19 +194,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset; boolean isRtl = isLayoutRtl(); int start = isRtl ? (mDisplaySize.x - right) : left; - if (start != targetPadding) { - if (mCutOutBounds != null) { - for (Rect cutOutRect : mCutOutBounds) { - int cutOutStart = (isRtl) - ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left; - if (start > cutOutStart) { - start -= cutOutRect.width(); - break; - } - } - } - int newPadding = targetPadding - start + getPaddingStart(); setPaddingRelative(newPadding, 0, mEndMargin, 0); } @@ -252,12 +237,6 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { getDisplaySize(); - mCutOutBounds = null; - if (displayCutout != null && displayCutout.getSafeInsetRight() == 0 - && displayCutout.getSafeInsetLeft() == 0) { - mCutOutBounds = displayCutout.getBoundingRects(); - } - // For Double Cut Out mode, the System window navigation bar is at the right // side of the left cut out. In this condition, mSysWinInset include the left cut // out width so we set mCutOutInset to be 0. For RTL, the condition is the same. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java index b6357b8dbd8d..045a1976d502 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -36,6 +36,12 @@ public interface NotificationShadeWindowController extends RemoteInputController */ default void registerCallback(StatusBarWindowCallback callback) {} + /** + * Unregisters a {@link StatusBarWindowCallback previous registered with + * {@link #registerCallback(StatusBarWindowCallback)}} + */ + default void unregisterCallback(StatusBarWindowCallback callback) {} + /** Notifies the registered {@link StatusBarWindowCallback} instances. */ default void notifyStateChangedCallbacks() {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 889dfde3a66d..9dc4ac952b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -358,9 +358,13 @@ public class NotificationShelf extends ActivatableNotificationView implements && anv instanceof ExpandableNotificationRow && ((ExpandableNotificationRow) anv).isHeadsUp(); + final boolean isHunGoingToShade = mAmbientState.isShadeExpanded() + && anv == mAmbientState.getTrackedHeadsUpRow(); + final boolean shouldUpdateCornerRoundness = viewStart < shelfStart && !mHostLayoutController.isViewAffectedBySwipe(anv) && !isUnlockedHeadsUp + && !isHunGoingToShade && !mAmbientState.isPulsing() && !mAmbientState.isDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt index 91415f2788a4..05afc57fbb59 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt @@ -83,6 +83,7 @@ class WiredChargingRippleController @Inject constructor( var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null) init { + pluggedIn = batteryController.isPluggedIn val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback { override fun onBatteryLevelChanged( level: Int, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 655ed4132726..33aa7c720b22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -26,14 +26,18 @@ import android.os.Process import android.provider.DeviceConfig import android.util.Log import android.view.View +import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.phone.StatusBarWindowController import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.util.Assert import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock +import java.io.FileDescriptor +import java.io.PrintWriter import javax.inject.Inject @@ -59,9 +63,10 @@ class SystemStatusAnimationScheduler @Inject constructor( private val coordinator: SystemEventCoordinator, private val chipAnimationController: SystemEventChipAnimationController, private val statusBarWindowController: StatusBarWindowController, + private val dumpManager: DumpManager, private val systemClock: SystemClock, @Main private val executor: DelayableExecutor -) : CallbackController<SystemStatusAnimationCallback> { +) : CallbackController<SystemStatusAnimationCallback>, Dumpable { companion object { private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator" @@ -71,10 +76,6 @@ class SystemStatusAnimationScheduler @Inject constructor( PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true) } - /** True from the time a scheduled event starts until it's animation finishes */ - var isActive = false - private set - @SystemAnimationState var animationState: Int = IDLE private set @@ -88,6 +89,7 @@ class SystemStatusAnimationScheduler @Inject constructor( init { coordinator.attachScheduler(this) + dumpManager.registerDumpable(TAG, this) } fun onStatusEvent(event: StatusEvent) { @@ -293,6 +295,20 @@ class SystemStatusAnimationScheduler @Inject constructor( anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState) } + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("Scheduled event: $scheduledEvent") + pw.println("Has persistent privacy dot: $hasPersistentDot") + pw.println("Animation state: $animationState") + pw.println("Listeners:") + if (listeners.isEmpty()) { + pw.println("(none)") + } else { + listeners.forEach { + pw.println(" $it") + } + } + } + inner class ChipAnimatorAdapter( @SystemAnimationState val endState: Int, val viewCreator: (context: Context) -> View diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 21d8164ba491..94edbd092a4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5145,8 +5145,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** - * Sets the extra top inset for the full shade transition. This is needed to compensate for - * media transitioning to quick settings + * Sets the extra top inset for the full shade transition. This moves notifications down + * during the drag down. */ public void setExtraTopInsetForFullShadeTransition(float inset) { mExtraTopInsetForFullShadeTransition = inset; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index d23a309ad1e9..4432f5463802 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -185,8 +185,17 @@ public class NotificationStackScrollLayoutController { private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; + /** + * The total distance in pixels that the full shade transition takes to transition entirely to + * the full shade. + */ private int mTotalDistanceForFullShadeTransition; - private int mTotalExtraMediaInsetFullShadeTransition; + + /** + * The amount of movement the notifications do when transitioning to the full shade before + * reaching the overstrech + */ + private int mNotificationDragDownMovement; @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -255,8 +264,8 @@ public class NotificationStackScrollLayoutController { }; private void updateResources() { - mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize( - R.dimen.lockscreen_shade_transition_extra_media_inset); + mNotificationDragDownMovement = mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_notification_movement); mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( R.dimen.lockscreen_shade_qs_transition_distance); } @@ -1410,15 +1419,13 @@ public class NotificationStackScrollLayoutController { * shade. 0.0f means we're not transitioning yet. */ public void setTransitionToFullShadeAmount(float amount) { - float extraTopInset; - MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer(); - if (view == null || view.getHeight() == 0 - || mStatusBarStateController.getState() != KEYGUARD) { - extraTopInset = 0; - } else { - extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition); - extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset); - extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition; + float extraTopInset = 0.0f; + if (mStatusBarStateController.getState() == KEYGUARD) { + float overallProgress = MathUtils.saturate(amount / mView.getHeight()); + float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress, + 0.6f, + (float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight()); + extraTopInset = transitionProgress * mNotificationDragDownMovement; } mView.setExtraTopInsetForFullShadeTransition(extraTopInset); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index d9cbfda8b3c8..86465b6f6b1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -380,11 +380,15 @@ public class StackScrollAlgorithm { ExpandableViewState viewState = view.getViewState(); viewState.location = ExpandableViewState.LOCATION_UNKNOWN; - if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) { - viewState.alpha = Interpolators.getNotificationScrimAlpha( - ambientState.getExpansionFraction(), true /* notification */); - } else { - viewState.alpha = 1f - ambientState.getHideAmount(); + final boolean isHunGoingToShade = ambientState.isShadeExpanded() + && view == ambientState.getTrackedHeadsUpRow(); + if (!isHunGoingToShade) { + if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) { + viewState.alpha = Interpolators.getNotificationScrimAlpha( + ambientState.getExpansionFraction(), true /* notification */); + } else { + viewState.alpha = 1f - ambientState.getHideAmount(); + } } if (view.mustStayOnScreen() && viewState.yTranslation >= 0) { @@ -414,13 +418,15 @@ public class StackScrollAlgorithm { } if (view instanceof FooterView) { + final boolean shadeClosed = !ambientState.isShadeExpanded(); final boolean isShelfShowing = algorithmState.firstViewInShelf != null; final float footerEnd = algorithmState.mCurrentExpandedYPosition + view.getIntrinsicHeight(); final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight(); - viewState.hidden = isShelfShowing || noSpaceForFooter; + viewState.hidden = shadeClosed || isShelfShowing || noSpaceForFooter; + } else if (view != ambientState.getTrackedHeadsUpRow()) { if (ambientState.isExpansionChanging()) { // Show all views. Views below the shelf will later be clipped (essentially hidden) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 7f919b5f5cf5..4d8e7de37606 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -24,6 +24,8 @@ import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTT import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -39,7 +41,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; -import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; @@ -49,13 +50,10 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; -import android.provider.Settings; import android.service.media.CameraPrewarmService; import android.service.quickaccesswallet.GetWalletCardsError; -import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.QuickAccessWalletClient; -import android.service.quickaccesswallet.QuickAccessWalletClientImpl; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.AttributeSet; @@ -71,7 +69,6 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; @@ -97,11 +94,9 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PreviewInflater; import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory; import com.android.systemui.tuner.TunerService; -import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.systemui.wallet.ui.WalletActivity; -import java.util.concurrent.Executor; - /** * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status * text. @@ -137,10 +132,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mLeftAffordanceView; private ImageView mWalletButton; - private boolean mWalletEnabled = false; private boolean mHasCard = false; private WalletCardRetriever mCardRetriever = new WalletCardRetriever(); - private QuickAccessWalletClient mQuickAccessWalletClient; + private QuickAccessWalletController mQuickAccessWalletController; private ViewGroup mIndicationArea; private TextView mIndicationText; @@ -159,7 +153,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private StatusBar mStatusBar; private KeyguardAffordanceHelper mAffordanceHelper; private FalsingManager mFalsingManager; - @Nullable private Executor mUiExecutor; private boolean mUserSetupComplete; private boolean mPrewarmBound; private Messenger mPrewarmMessenger; @@ -193,8 +186,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private int mBurnInYOffset; private ActivityIntentHelper mActivityIntentHelper; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private ContentObserver mWalletPreferenceObserver; - private SecureSettings mSecureSettings; public KeyguardBottomAreaView(Context context) { this(context, null); @@ -332,8 +323,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL getContext().unregisterReceiver(mDevicePolicyReceiver); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); - if (mWalletPreferenceObserver != null) { - mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver); + if (mQuickAccessWalletController != null) { + mQuickAccessWalletController.unregisterWalletChangeObservers( + WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); } } @@ -456,7 +448,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateWalletVisibility() { - if (mDozing || !mWalletEnabled || !mHasCard) { + if (mDozing + || mQuickAccessWalletController == null + || !mQuickAccessWalletController.isWalletEnabled() + || !mHasCard) { mWalletButton.setVisibility(GONE); mIndicationArea.setPadding(0, 0, 0, 0); } else { @@ -690,7 +685,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onKeyguardShowingChanged() { if (mKeyguardStateController.isShowing()) { - queryWalletCards(); + if (mQuickAccessWalletController != null) { + mQuickAccessWalletController.queryWalletCards(mCardRetriever); + } } } @@ -935,50 +932,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL /** * Initialize the wallet feature, only enabling if the feature is enabled within the platform. */ - public void initWallet(QuickAccessWalletClient client, Executor uiExecutor, - SecureSettings secureSettings) { - mQuickAccessWalletClient = client; - mSecureSettings = secureSettings; - setupWalletPreferenceObserver(); - updateWalletPreference(); - - mUiExecutor = uiExecutor; - queryWalletCards(); + public void initWallet( + QuickAccessWalletController controller) { + mQuickAccessWalletController = controller; + mQuickAccessWalletController.setupWalletChangeObservers( + mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); + mQuickAccessWalletController.updateWalletPreference(); + mQuickAccessWalletController.queryWalletCards(mCardRetriever); updateWalletVisibility(); } - private void setupWalletPreferenceObserver() { - if (mWalletPreferenceObserver == null) { - mWalletPreferenceObserver = new ContentObserver(null /* handler */) { - @Override - public void onChange(boolean selfChange) { - mUiExecutor.execute(() -> updateWalletPreference()); - } - }; - - mSecureSettings.registerContentObserver( - Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY), - false /* notifyForDescendants */, - mWalletPreferenceObserver); - } - } - - private void updateWalletPreference() { - mWalletEnabled = mQuickAccessWalletClient.isWalletFeatureAvailable() - && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked(); - } - - private void queryWalletCards() { - if (!mWalletEnabled || mUiExecutor == null) { - return; - } - GetWalletCardsRequest request = - new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */, - 1 /* iconSizePx */, 1 /* maxCards */); - mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever); - } - private void onWalletClick(View v) { // More coming here; need to inform the user about how to proceed if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { @@ -991,12 +955,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } else { - if (mQuickAccessWalletClient.createWalletIntent() == null) { + if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) { Log.w(TAG, "Could not get intent of the wallet app."); return; } mActivityStarter.postStartActivityDismissingKeyguard( - mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0); + mQuickAccessWalletController.getWalletClient().createWalletIntent(), + /* delay= */ 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index f4710f49524d..7c2723d724ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -325,7 +325,9 @@ public class KeyguardClockPositionAlgorithm { */ private float getClockAlpha(int y) { float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount))); - alphaKeyguard *= (1f - mQsExpansion); + float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f); + qsAlphaFactor = 1f - qsAlphaFactor; + alphaKeyguard *= qsAlphaFactor; alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard); return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index d84bb908fe69..68e20705fbeb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -142,7 +142,6 @@ public class KeyguardIndicationTextView extends TextView { Animator yTranslate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -getYTranslationPixels()); yTranslate.setDuration(getFadeOutDuration()); - fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); animatorSet.playTogether(fadeOut, yTranslate); return animatorSet; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 0c4bec2c2c65..eef24200a882 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -116,6 +116,12 @@ public class KeyguardStatusBarView extends RelativeLayout implements // right and left padding applied to this view to account for cutouts and rounded corners private Pair<Integer, Integer> mPadding = new Pair(0, 0); + /** + * The clipping on the top + */ + private int mTopClipping; + private final Rect mClipRect = new Rect(0, 0, 0, 0); + public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); mUserManager = UserManager.get(getContext()); @@ -549,4 +555,25 @@ public class KeyguardStatusBarView extends RelativeLayout implements public void onSystemChromeAnimationUpdate(ValueAnimator anim) { mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue()); } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + updateClipping(); + } + + /** + * Set the clipping on the top of the view. + */ + public void setTopClipping(int topClipping) { + if (topClipping != mTopClipping) { + mTopClipping = topClipping; + updateClipping(); + } + } + + private void updateClipping() { + mClipRect.set(0, mTopClipping, getWidth(), getHeight()); + setClipBounds(mClipRect); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 0f3af095f7be..d9ba494a4d63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -24,6 +24,8 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; +import com.android.systemui.R; + public class NotificationPanelView extends PanelView { private static final boolean DEBUG = false; @@ -92,6 +94,10 @@ public class NotificationPanelView extends PanelView { mRtlChangeListener = listener; } + public TapAgainView getTapAgainView() { + return findViewById(R.id.shade_falsing_tap_again); + } + interface RtlChangeListener { void onRtlPropertielsChanged(int layoutDirection); } 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 1263b104b6a5..efcde46a7fb7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -54,7 +54,6 @@ import android.os.PowerManager; import android.os.SystemClock; import android.os.UserManager; import android.os.VibrationEffect; -import android.service.quickaccesswallet.QuickAccessWalletClient; import android.util.Log; import android.util.MathUtils; import android.view.DisplayCutout; @@ -97,8 +96,8 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; -import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; @@ -153,6 +152,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.Utils; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.FileDescriptor; @@ -306,14 +306,17 @@ public class NotificationPanelViewController extends PanelViewController { private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; private final QSDetailDisplayer mQSDetailDisplayer; + private final FragmentService mFragmentService; 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 private final int mMaxKeyguardNotifications; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; + private final TapAgainViewController mTapAgainViewController; private boolean mShouldUseSplitNotificationShade; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -508,6 +511,13 @@ public class NotificationPanelViewController extends PanelViewController { private float mSectionPadding; /** + * The padding between the start of notifications and the qs boundary on the lockscreen. + * On lockscreen, notifications aren't inset this extra amount, but we still want the + * qs boundary to be padded. + */ + private int mLockscreenNotificationQSPadding; + + /** * The amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full * shade. This value can also go beyond 1.1 when we're overshooting! @@ -529,7 +539,7 @@ public class NotificationPanelViewController extends PanelViewController { /** * The maximum overshoot allowed for the top padding for the full shade transition */ - private int mMaxOverscrollAmountForDragDown; + private int mMaxOverscrollAmountForPulse; /** * Should we animate the next bounds update @@ -573,8 +583,8 @@ public class NotificationPanelViewController extends PanelViewController { private int mScrimCornerRadius; private int mScreenCornerRadius; private int mNotificationScrimPadding; + private boolean mQSAnimatingHiddenFromCollapsed; - private final QuickAccessWalletClient mQuickAccessWalletClient; private final Executor mUiExecutor; private final SecureSettings mSecureSettings; @@ -604,7 +614,12 @@ public class NotificationPanelViewController extends PanelViewController { private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { @Override public void onDoubleTapRequired() { - showTransientIndication(R.string.notification_tap_again); + if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { + mTapAgainViewController.show(); + } else { + mKeyguardIndicationController.showTransientIndication( + R.string.notification_tap_again); + } mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); } }; @@ -650,9 +665,11 @@ public class NotificationPanelViewController extends PanelViewController { AmbientState ambientState, LockIconViewController lockIconViewController, FeatureFlags featureFlags, - QuickAccessWalletClient quickAccessWalletClient, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, + TapAgainViewController tapAgainViewController, + FragmentService fragmentService, + QuickAccessWalletController quickAccessWalletController, @Main Executor uiExecutor, SecureSettings secureSettings) { super(view, falsingManager, dozeLog, keyguardStateController, @@ -663,6 +680,7 @@ public class NotificationPanelViewController extends PanelViewController { mVibratorHelper = vibratorHelper; mKeyguardMediaController = keyguardMediaController; mPrivacyDotViewController = privacyDotViewController; + mQuickAccessWalletController = quickAccessWalletController; mMetricsLogger = metricsLogger; mActivityManager = activityManager; mConfigurationController = configurationController; @@ -679,6 +697,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; mQSDetailDisplayer = qsDetailDisplayer; + mFragmentService = fragmentService; mKeyguardUserSwitcherEnabled = mResources.getBoolean( com.android.internal.R.bool.config_keyguardUserSwitcher); mKeyguardQsUserSwitchEnabled = @@ -704,7 +723,7 @@ public class NotificationPanelViewController extends PanelViewController { mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); mUserManager = userManager; mMediaDataManager = mediaDataManager; - mQuickAccessWalletClient = quickAccessWalletClient; + mTapAgainViewController = tapAgainViewController; mUiExecutor = uiExecutor; mSecureSettings = secureSettings; pulseExpansionHandler.setPulseExpandAbortListener(() -> { @@ -812,7 +831,7 @@ public class NotificationPanelViewController extends PanelViewController { amount -> { float progress = amount / mView.getHeight(); float overstretch = Interpolators.getOvershootInterpolation(progress, - (float) mMaxOverscrollAmountForDragDown / mView.getHeight(), + (float) mMaxOverscrollAmountForPulse / mView.getHeight(), 0.2f); setOverStrechAmount(overstretch); }); @@ -843,6 +862,8 @@ public class NotificationPanelViewController extends PanelViewController { if (mShouldUseSplitNotificationShade) { updateResources(); } + + mTapAgainViewController.init(); } @Override @@ -873,14 +894,16 @@ public class NotificationPanelViewController extends PanelViewController { R.dimen.heads_up_status_bar_padding); mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize( R.dimen.lockscreen_shade_qs_transition_distance); - mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize( - R.dimen.lockscreen_shade_max_top_overshoot); + mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize( + R.dimen.pulse_expansion_max_top_overshoot); mScrimCornerRadius = mResources.getDimensionPixelSize( R.dimen.notification_scrim_corner_radius); mScreenCornerRadius = mResources.getDimensionPixelSize( com.android.internal.R.dimen.rounded_corner_radius); mNotificationScrimPadding = mResources.getDimensionPixelSize( R.dimen.notification_side_paddings); + mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( + R.dimen.notification_side_paddings); } private void updateViewControllers(KeyguardStatusView keyguardStatusView, @@ -1100,7 +1123,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea.setFalsingManager(mFalsingManager); if (mFeatureFlags.isQuickAccessWalletEnabled()) { - mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor, mSecureSettings); + mKeyguardBottomArea.initWallet(mQuickAccessWalletController); } } @@ -1421,7 +1444,7 @@ public class NotificationPanelViewController extends PanelViewController { } mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); - if (animate) { + if (animate && !isFullyCollapsed()) { animateCloseQs(true /* animateAway */); } else { closeQs(); @@ -1705,6 +1728,11 @@ public class NotificationPanelViewController extends PanelViewController { } private float computeQsExpansionFraction() { + if (mQSAnimatingHiddenFromCollapsed) { + // When hiding QS from collapsed state, the expansion can sometimes temporarily + // be larger than 0 because of the timing, leading to flickers. + return 0.0f; + } return Math.min( 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight - mQsMinExpansionHeight)); @@ -2238,19 +2266,24 @@ public class NotificationPanelViewController extends PanelViewController { boolean visible) { // Fancy clipping for quick settings int radius = mScrimCornerRadius; + int statusBarClipTop = 0; + boolean clipStatusView = false; if (!mShouldUseSplitNotificationShade) { // The padding on this area is large enough that we can use a cheaper clipping strategy mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); - mKeyguardStatusViewController.setClipBounds(visible - ? mKeyguardStatusAreaClipBounds : null); + clipStatusView = visible; radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); + statusBarClipTop = top - mKeyguardStatusBar.getTop(); } if (mQs != null) { mQs.setFancyClipping(top, bottom, radius, visible); } + mKeyguardStatusViewController.setClipBounds( + clipStatusView ? mKeyguardStatusAreaClipBounds : null); mScrimController.setNotificationsBounds(left, top, right, bottom); mScrimController.setScrimCornerRadius(radius); + mKeyguardStatusBar.setTopClipping(statusBarClipTop); } private float getQSEdgePosition() { @@ -2367,7 +2400,6 @@ public class NotificationPanelViewController extends PanelViewController { public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) { mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade; mNotificationBoundsAnimationDelay = delay; - float progress = MathUtils.saturate(pxAmount / mView.getHeight()); float endPosition = 0; if (pxAmount > 0.0f) { @@ -2375,29 +2407,28 @@ public class NotificationPanelViewController extends PanelViewController { && !mMediaDataManager.hasActiveMedia()) { // No notifications are visible, let's animate to the height of qs instead if (mQs != null) { - // Let's interpolate to the header height - endPosition = mQs.getHeader().getHeight(); + // Let's interpolate to the header height instead of the top padding, + // because the toppadding is way too low because of the large clock. + // we still want to take into account the edgePosition though as that nicely + // overshoots in the stackscroller + endPosition = getQSEdgePosition() + - mNotificationStackScrollLayoutController.getTopPadding() + + mQs.getHeader().getHeight(); } } else { // Interpolating to the new bottom edge position! - endPosition = getQSEdgePosition() - mOverStretchAmount; - - // If we have media, we need to put the boundary below it, as the media header - // still uses the space during the transition. - endPosition += - mNotificationStackScrollLayoutController.getFullShadeTransitionInset(); + endPosition = getQSEdgePosition() + + mNotificationStackScrollLayoutController.getFullShadeTransitionInset(); + if (isOnKeyguard()) { + endPosition -= mLockscreenNotificationQSPadding; + } } } // Calculate the overshoot amount such that we're reaching the target after our desired // distance, but only reach it fully once we drag a full shade length. - float transitionProgress = 0; - if (endPosition != 0 && progress != 0) { - transitionProgress = Interpolators.getOvershootInterpolation(progress, - mMaxOverscrollAmountForDragDown / endPosition, - (float) mDistanceForQSFullShadeTransition / (float) mView.getHeight()); - } - mTransitioningToFullShadeProgress = transitionProgress; + mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( + MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition)); int position = (int) MathUtils.lerp((float) 0, endPosition, mTransitioningToFullShadeProgress); @@ -2405,8 +2436,6 @@ public class NotificationPanelViewController extends PanelViewController { // we want at least 1 pixel otherwise the panel won't be clipped position = Math.max(1, position); } - float overStretchAmount = Math.max(position - endPosition, 0.0f); - setOverStrechAmount(overStretchAmount); mTransitionToFullShadeQSPosition = position; updateQsExpansion(); } @@ -2509,6 +2538,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onAnimationEnd(Animator animation) { + mQSAnimatingHiddenFromCollapsed = false; mAnimatingQS = false; notifyExpandingFinished(); mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); @@ -2525,6 +2555,7 @@ public class NotificationPanelViewController extends PanelViewController { animator.start(); mQsExpansionAnimator = animator; mQsAnimatorExpand = expanding; + mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0; } /** @@ -3675,10 +3706,6 @@ public class NotificationPanelViewController extends PanelViewController { updateMaxDisplayedNotifications(true); } - public void showTransientIndication(int id) { - mKeyguardIndicationController.showTransientIndication(id); - } - public void setAlpha(float alpha) { mView.setAlpha(alpha); } @@ -4264,7 +4291,8 @@ public class NotificationPanelViewController extends PanelViewController { private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { @Override public void onViewAttachedToWindow(View v) { - FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); + mFragmentService.getFragmentHostManager(mView) + .addTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); @@ -4278,7 +4306,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onViewDetachedFromWindow(View v) { - FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener); + mFragmentService.getFragmentHostManager(mView) + .removeTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index ae018ba4fe76..52f9aca82783 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -152,6 +152,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback)); } + @Override + public void unregisterCallback(StatusBarWindowCallback callback) { + for (int i = 0; i < mCallbacks.size(); i++) { + if (mCallbacks.get(i).get() == callback) { + mCallbacks.remove(i); + return; + } + } + } + /** * Register a listener to monitor scrims visibility * @param listener A listener to monitor scrims visibility diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index ba2340e76831..c27497e43400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -464,6 +464,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final BrightnessSlider.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; + private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final List<ExpansionChangedListener> mExpansionChangedListeners; @@ -885,6 +886,8 @@ public class StatusBar extends SystemUI implements DemoMode, mAnimationScheduler = animationScheduler; mStatusBarLocationPublisher = locationPublisher; mFeatureFlags = featureFlags; + mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; lockscreenShadeTransitionController.setStatusbar(this); @@ -1369,7 +1372,8 @@ public class StatusBar extends SystemUI implements DemoMode, // are already animating the keyguard dismiss (since we will need to either finish or cancel // the animation). if (trackingTouch - || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) { + || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe() + || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) { mKeyguardStateController.notifyKeyguardDismissAmountChanged( 1f - expansion, trackingTouch); } @@ -4386,6 +4390,7 @@ public class StatusBar extends SystemUI implements DemoMode, } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } + updateLightRevealScrimVisibility(); Trace.endSection(); } @@ -4818,6 +4823,11 @@ public class StatusBar extends SystemUI implements DemoMode, return; } + if (mDozeServiceHost.isPulsing()) { + mLightRevealScrim.setVisibility(View.GONE); + return; + } + if (mFeatureFlags.useNewLockscreenAnimations() && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) { mLightRevealScrim.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index 8befe800b4e1..edcf261115d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -23,6 +23,8 @@ import android.util.Pair import android.view.DisplayCutout import android.view.View.LAYOUT_DIRECTION_RTL import android.view.WindowManager +import android.view.WindowMetrics +import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton @@ -118,17 +120,8 @@ class StatusBarContentInsetsProvider @Inject constructor( val chipWidth = rotatedResources.getDimensionPixelSize( R.dimen.ongoing_appops_chip_max_width) - return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) { - Rect(insets.left - dotWidth, - insets.top, - insets.left + chipWidth, - insets.bottom) - } else { - Rect(insets.right - chipWidth, - insets.top, - insets.right + dotWidth, - insets.bottom) - } + val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL + return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl) } /** @@ -139,8 +132,7 @@ class StatusBarContentInsetsProvider @Inject constructor( var insets = insetsByCorner[rotation] if (insets == null) { val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context) - insets = getCalculatedInsetsForRotation(rotation, rotatedResources) - insetsByCorner[rotation] = insets + insets = getAndSetInsetsForRotation(rotation, rotatedResources) } return insets @@ -157,13 +149,19 @@ class StatusBarContentInsetsProvider @Inject constructor( } private fun getCalculatedInsetsForRotation( - @Rotation rotation: Int, + @Rotation targetRotation: Int, rotatedResources: Resources ): Rect { val dc = context.display.cutout + val currentRotation = RotationUtils.getExactRotation(context) return calculateInsetsForRotationWithRotatedResources( - rotation, rotatedResources, dc, windowManager, context) + currentRotation, + targetRotation, + dc, + windowManager.maximumWindowMetrics, + rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height), + rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { @@ -179,8 +177,8 @@ interface StatusBarContentInsetsChangedListener { private const val TAG = "StatusBarInsetsProvider" -private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect { - val bounds = wm.maximumWindowMetrics.bounds +private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect { + val bounds = wm.bounds if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) { return bounds @@ -190,9 +188,24 @@ private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotat return Rect(0, 0, bounds.bottom, bounds.right) } -private fun getCurrentDisplayBounds(wm: WindowManager): Rect { - val bounds = wm.maximumWindowMetrics.bounds - return bounds +@VisibleForTesting +fun getPrivacyChipBoundingRectForInsets( + contentRect: Rect, + dotWidth: Int, + chipWidth: Int, + isRtl: Boolean +): Rect { + return if (isRtl) { + Rect(contentRect.left - dotWidth, + contentRect.top, + contentRect.left + chipWidth, + contentRect.bottom) + } else { + Rect(contentRect.right - chipWidth, + contentRect.top, + contentRect.right + dotWidth, + contentRect.bottom) + } } /** @@ -206,41 +219,32 @@ private fun getCurrentDisplayBounds(wm: WindowManager): Rect { * @see [RotationUtils#getResourcesForRotation] */ fun calculateInsetsForRotationWithRotatedResources( + @Rotation currentRotation: Int, @Rotation targetRotation: Int, - rotatedResources: Resources, displayCutout: DisplayCutout?, - windowmanager: WindowManager, - context: Context + windowMetrics: WindowMetrics, + statusBarHeight: Int, + roundedCornerPadding: Int ): Rect { - val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL - - val exactRotation = RotationUtils.getExactRotation(context) - val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height) - /* TODO: Check if this is ever used for devices with no rounded corners - val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start) - val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end) - val left = if (rtl) paddingEnd else paddingStart - val right = if(rtl) paddingStart else paddingEnd + val left = if (isRtl) paddingEnd else paddingStart + val right = if (isRtl) paddingStart else paddingEnd */ - val roundedCornerPadding = rotatedResources.getDimensionPixelSize( - R.dimen.rounded_corner_content_padding) - - val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation) - val currentBounds = getCurrentDisplayBounds(windowmanager) + val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation) + val currentBounds = windowMetrics.bounds val sbLeftRight = getStatusBarLeftRight( displayCutout, - height, + statusBarHeight, rotZeroBounds.right, rotZeroBounds.bottom, currentBounds.width(), currentBounds.height(), roundedCornerPadding, targetRotation, - exactRotation) + currentRotation) return sbLeftRight } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index ebf2465f3bf3..d3953df8c8c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -23,7 +23,6 @@ import android.util.ArraySet; import android.util.Log; import com.android.settingslib.mobile.TelephonyIcons; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.NetworkController; @@ -63,6 +62,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba private final SecurityController mSecurityController; private final Handler mHandler = Handler.getMain(); private final CarrierConfigTracker mCarrierConfigTracker; + private final TunerService mTunerService; private boolean mHideAirplane; private boolean mHideMobile; @@ -83,9 +83,16 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba @Inject public StatusBarSignalPolicy(Context context, StatusBarIconController iconController, - CarrierConfigTracker carrierConfigTracker) { + CarrierConfigTracker carrierConfigTracker, NetworkController networkController, + SecurityController securityController, TunerService tunerService) { mContext = context; + mIconController = iconController; + mCarrierConfigTracker = carrierConfigTracker; + mNetworkController = networkController; + mSecurityController = securityController; + mTunerService = tunerService; + mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); @@ -96,18 +103,14 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba mContext.getString(com.android.internal.R.string.status_bar_call_strength); mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); - mIconController = iconController; - mCarrierConfigTracker = carrierConfigTracker; - mNetworkController = Dependency.get(NetworkController.class); - mSecurityController = Dependency.get(SecurityController.class); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST); + tunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST); mNetworkController.addCallback(this); mSecurityController.addCallback(this); } public void destroy() { - Dependency.get(TunerService.class).removeTunable(this); + mTunerService.removeTunable(this); mNetworkController.removeCallback(this); mSecurityController.removeCallback(this); } @@ -172,7 +175,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba && !mIsAirplaneMode) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noValidatedNetwork && !mWifiIconState.noNetworksAvailable + } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_available; @@ -377,7 +380,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (noValidatedNetwork && !noNetworksAvailable + } else if (noDefaultNetwork && !noNetworksAvailable && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { newState.visible = true; newState.resId = R.drawable.ic_qs_no_internet_available; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java new file mode 100644 index 000000000000..52e0e8a7a0cb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java @@ -0,0 +1,110 @@ +/* + * 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.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.systemui.R; +import com.android.wm.shell.animation.Interpolators; + +/** + * View to show a toast-like popup on the notification shade and quick settings. + */ +public class TapAgainView extends TextView { + private TextView mTextView; + + public TapAgainView( + @NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + updateColor(); + } + + 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. */ + public void animateIn() { + int yTranslation = mContext.getResources().getDimensionPixelSize( + R.dimen.keyguard_indication_y_translation); + + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeIn = ObjectAnimator.ofFloat(this, View.ALPHA, 1f); + fadeIn.setStartDelay(150); // From KeyguardIndicationTextView#getFadeInDelay + fadeIn.setDuration(317); // From KeyguardIndicationTextView#getFadeInDuration + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, yTranslation, 0); + yTranslate.setDuration(600); // From KeyguardIndicationTextView#getYInDuration + yTranslate.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + setTranslationY(0); + } + }); + animatorSet.playTogether(yTranslate, fadeIn); + animatorSet.start(); + setVisibility(View.VISIBLE); + } + + /** Make the view gone. */ + public void animateOut() { + long fadeOutDuration = 167L; // From KeyguardIndicationTextView#getFadeOutDuration + int yTranslation = mContext.getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.keyguard_indication_y_translation); + + AnimatorSet animatorSet = new AnimatorSet(); + ObjectAnimator fadeOut = ObjectAnimator.ofFloat(this, View.ALPHA, 0f); + fadeOut.setDuration(fadeOutDuration); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + + Animator yTranslate = + ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0, -yTranslation); + yTranslate.setDuration(fadeOutDuration); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + setVisibility(GONE); + } + }); + animatorSet.playTogether(yTranslate, fadeOut); + animatorSet.start(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java new file mode 100644 index 000000000000..0c5502bac8fc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java @@ -0,0 +1,96 @@ +/* + * 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 static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.util.ViewController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Controller for {@link TapAgainView}. + */ +@StatusBarComponent.StatusBarScope +public class TapAgainViewController extends ViewController<TapAgainView> { + private final DelayableExecutor mDelayableExecutor; + private final ConfigurationController mConfigurationController; + private final long mDoubleTapTimeMs; + + private Runnable mHideCanceler; + + @VisibleForTesting + final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onOverlayChanged() { + mView.updateColor(); + } + + @Override + public void onUiModeChanged() { + mView.updateColor(); + } + + @Override + public void onThemeChanged() { + mView.updateColor(); + } + }; + + @Inject + protected TapAgainViewController(TapAgainView view, + @Main DelayableExecutor delayableExecutor, + ConfigurationController configurationController, + @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) { + super(view); + mDelayableExecutor = delayableExecutor; + mConfigurationController = configurationController; + mDoubleTapTimeMs = doubleTapTimeMs; + } + + @Override + protected void onViewAttached() { + mConfigurationController.addCallback(mConfigurationListener); + } + + @Override + protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); + } + + /** Shows the associated view, possibly animating it. */ + public void show() { + if (mHideCanceler != null) { + mHideCanceler.run(); + } + mView.animateIn(); + mHideCanceler = mDelayableExecutor.executeDelayed(this::hide, mDoubleTapTimeMs); + } + + /** Hides the associated view, possibly animating it. */ + public void hide() { + mHideCanceler = null; + mView.animateOut(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 008c0aea7ce9..27d71edd5e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -23,6 +23,7 @@ import com.android.systemui.R; import com.android.systemui.biometrics.AuthRippleView; import com.android.systemui.statusbar.phone.NotificationPanelView; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; +import com.android.systemui.statusbar.phone.TapAgainView; import dagger.Module; import dagger.Provides; @@ -53,4 +54,11 @@ public abstract class StatusBarViewModule { NotificationShadeWindowView notificationShadeWindowView) { return notificationShadeWindowView.findViewById(R.id.auth_ripple); } + + /** */ + @Provides + @StatusBarComponent.StatusBarScope + public static TapAgainView getTapAgainView(NotificationPanelView npv) { + return npv.getTapAgainView(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java index e7201f87b320..af7bf9500bf3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java @@ -50,6 +50,13 @@ public interface KeyguardStateController extends CallbackController<Callback> { boolean canDismissLockScreen(); /** + * Whether we can currently perform the shared element SmartSpace transition. This is true if + * we're on the lockscreen, it can be dismissed with a swipe, and the Launcher is underneath the + * keyguard and displaying a SmartSpace that it has registered with System UI. + */ + boolean canPerformSmartSpaceTransition(); + + /** * If the device has PIN/pattern/password or a lock screen at all. */ boolean isMethodSecure(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index e69c1f2573b7..0945a3f884d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -32,6 +32,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -53,6 +54,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private final LockPatternUtils mLockPatternUtils; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new UpdateMonitorCallback(); + private final SmartspaceTransitionController mSmartspaceTransitionController; private boolean mCanDismissLockScreen; private boolean mShowing; @@ -96,10 +98,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum */ @Inject public KeyguardStateControllerImpl(Context context, - KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { + KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils, + SmartspaceTransitionController smartspaceTransitionController) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + mSmartspaceTransitionController = smartspaceTransitionController; update(true /* updateAlways */); if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) { @@ -158,6 +162,11 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum mShowing = showing; mOccluded = occluded; notifyKeyguardChanged(); + + // Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard. + // Otherwise, the dismiss amount could be left at a random value if we show/hide during a + // dismiss gesture, canceling the gesture. + notifyKeyguardDismissAmountChanged(showing ? 0f : 1f, false); } private void notifyKeyguardChanged() { @@ -228,6 +237,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override + public boolean canPerformSmartSpaceTransition() { + return canDismissLockScreen() + && mSmartspaceTransitionController.isSmartspaceTransitionPossible(); + } + + @Override public boolean isFaceAuthEnabled() { return mFaceAuthEnabled; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 07e9fed13499..4ab07af92006 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -1023,6 +1023,11 @@ public class NetworkControllerImpl extends BroadcastReceiver mValidatedTransports.clear(); if (mLastDefaultNetworkCapabilities != null) { for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) { + if (transportType != NetworkCapabilities.TRANSPORT_CELLULAR + && transportType != NetworkCapabilities.TRANSPORT_WIFI + && transportType != NetworkCapabilities.TRANSPORT_ETHERNET) { + continue; + } if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) { mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); @@ -1045,11 +1050,15 @@ public class NetworkControllerImpl extends BroadcastReceiver Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); } - mInetCondition = !mValidatedTransports.isEmpty(); + mInetCondition = mValidatedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); pushConnectivityToSignals(); if (mProviderModel) { - mNoDefaultNetwork = mConnectedTransports.isEmpty(); + mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI) + && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable); for (int i = 0; i < mMobileSignalControllers.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index 4284148c38a8..c5e35a497956 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -22,16 +22,19 @@ import android.annotation.Nullable; import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import android.widget.ToastPresenter; import com.android.internal.R; import com.android.launcher3.icons.IconFactory; @@ -52,7 +55,6 @@ public class SystemUIToast implements ToastPlugin.Toast { private final String mPackageName; private final int mUserId; private final LayoutInflater mLayoutInflater; - private final boolean mToastStyleEnabled; final int mDefaultX = 0; final int mDefaultHorizontalMargin = 0; @@ -66,15 +68,14 @@ public class SystemUIToast implements ToastPlugin.Toast { @Nullable private final Animator mOutAnimator; SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, - String packageName, int userId, boolean toastStyleEnabled, int orientation) { + String packageName, int userId, int orientation) { this(layoutInflater, context, text, null, packageName, userId, - toastStyleEnabled, orientation); + orientation); } SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, ToastPlugin.Toast pluginToast, String packageName, int userId, - boolean toastStyleEnabled, int orientation) { - mToastStyleEnabled = toastStyleEnabled; + int orientation) { mLayoutInflater = layoutInflater; mContext = context; mText = text; @@ -167,23 +168,45 @@ public class SystemUIToast implements ToastPlugin.Toast { return mPluginToast.getView(); } - View toastView; - if (mToastStyleEnabled) { - toastView = mLayoutInflater.inflate( + final View toastView = mLayoutInflater.inflate( com.android.systemui.R.layout.text_toast, null); - ((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText); + final TextView textView = toastView.findViewById(com.android.systemui.R.id.text); + final ImageView iconView = toastView.findViewById(com.android.systemui.R.id.icon); + textView.setText(mText); + + ApplicationInfo appInfo = null; + try { + appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(mPackageName, 0, mUserId); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Package name not found package=" + mPackageName + + " user=" + mUserId); + } + + if (appInfo != null && appInfo.targetSdkVersion < Build.VERSION_CODES.S) { + // no two-line limit + textView.setMaxLines(Integer.MAX_VALUE); + // no app icon + toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE); + } else { Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId); if (icon == null) { - toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE); + iconView.setVisibility(View.GONE); } else { - ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon)) - .setImageDrawable(icon); + iconView.setImageDrawable(icon); + if (appInfo.labelRes != 0) { + try { + Resources res = mContext.getPackageManager().getResourcesForApplication( + appInfo, + new Configuration(mContext.getResources().getConfiguration())); + iconView.setContentDescription(res.getString(appInfo.labelRes)); + } catch (PackageManager.NameNotFoundException e) { + Log.d(TAG, "Cannot find application resources for icon label."); + } + } } - } else { - toastView = ToastPresenter.getTextToastView(mContext, mText); } - return toastView; } @@ -205,18 +228,14 @@ public class SystemUIToast implements ToastPlugin.Toast { return mPluginToast.getInAnimation(); } - return mToastStyleEnabled - ? ToastDefaultAnimation.Companion.toastIn(getView()) - : null; + return ToastDefaultAnimation.Companion.toastIn(getView()); } private Animator createOutAnimator() { if (isPluginToast() && mPluginToast.getOutAnimation() != null) { return mPluginToast.getOutAnimation(); } - return mToastStyleEnabled - ? ToastDefaultAnimation.Companion.toastOut(getView()) - : null; + return ToastDefaultAnimation.Companion.toastOut(getView()); } /** @@ -225,6 +244,10 @@ public class SystemUIToast implements ToastPlugin.Toast { */ public static Drawable getBadgedIcon(@NonNull Context context, String packageName, int userId) { + if (!(context.getApplicationContext() instanceof Application)) { + return null; + } + final ApplicationsState appState = ApplicationsState.getInstance((Application) context.getApplicationContext()); if (!appState.isUserAdded(userId)) { diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java index 8b782d4b7923..148bffa0000e 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java @@ -27,7 +27,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.ToastPlugin; import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.statusbar.FeatureFlags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -43,17 +42,14 @@ public class ToastFactory implements Dumpable { // only one ToastPlugin can be connected at a time. private ToastPlugin mPlugin; private final LayoutInflater mLayoutInflater; - private final boolean mToastStyleEnabled; @Inject public ToastFactory( LayoutInflater layoutInflater, PluginManager pluginManager, - DumpManager dumpManager, - FeatureFlags featureFlags) { + DumpManager dumpManager) { mLayoutInflater = layoutInflater; dumpManager.registerDumpable("ToastFactory", this); - mToastStyleEnabled = featureFlags.isToastStyleEnabled(); pluginManager.addPluginListener( new PluginListener<ToastPlugin>() { @Override @@ -77,10 +73,10 @@ public class ToastFactory implements Dumpable { int userId, int orientation) { if (isPluginAvailable()) { return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text, - packageName, userId), packageName, userId, mToastStyleEnabled, orientation); + packageName, userId), packageName, userId, orientation); } return new SystemUIToast(mLayoutInflater, context, text, packageName, userId, - mToastStyleEnabled, orientation); + orientation); } private boolean isPluginAvailable() { @@ -91,6 +87,5 @@ public class ToastFactory implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("ToastFactory:"); pw.println(" mAttachedPlugin=" + mPlugin); - pw.println(" mToastStyleEnabled=" + mToastStyleEnabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 92ea1d0e5fbd..42f66875e7a1 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -19,6 +19,7 @@ package com.android.systemui.toast; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.ToastPresenter; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.VisibleForTesting; + import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; @@ -60,11 +62,11 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private final AccessibilityManager mAccessibilityManager; private final ToastFactory mToastFactory; private final ToastLogger mToastLogger; - private SystemUIToast mToast; @Nullable private ToastPresenter mPresenter; @Nullable private ITransientNotificationCallback mCallback; private ToastOutAnimatorListener mToastOutAnimatorListener; + @VisibleForTesting SystemUIToast mToast; private int mOrientation = ORIENTATION_PORTRAIT; @Inject @@ -191,7 +193,7 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { /** * Once the out animation for a toast is finished, start showing the next toast. */ - class ToastOutAnimatorListener implements Animator.AnimatorListener { + class ToastOutAnimatorListener extends AnimatorListenerAdapter { final ToastPresenter mPrevPresenter; final ITransientNotificationCallback mPrevCallback; @Nullable Runnable mShowNextToastRunnable; @@ -210,10 +212,6 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { } @Override - public void onAnimationStart(Animator animation) { - } - - @Override public void onAnimationEnd(Animator animation) { mPrevPresenter.hide(mPrevCallback); if (mShowNextToastRunnable != null) { @@ -221,15 +219,5 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { } mToastOutAnimatorListener = null; } - - @Override - public void onAnimationCancel(Animator animation) { - onAnimationEnd(animation); - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } } } diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java index 7a5ceb547d9e..fbec9e7b7f0c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactory.java @@ -30,12 +30,20 @@ import java.util.concurrent.Executor; */ public interface ThreadFactory { /** + * Returns a {@link Looper} running on a named thread. + * + * The thread is implicitly started and may be left running indefinitely, depending on the + * implementation. Assume this is the case and use responsibly. + */ + Looper buildLooperOnNewThread(String threadName); + + /** * Returns a {@link Handler} running on a named thread. * * The thread is implicitly started and may be left running indefinitely, depending on the * implementation. Assume this is the case and use responsibly. */ - Handler builderHandlerOnNewThread(String threadName); + Handler buildHandlerOnNewThread(String threadName); /** * Return an {@link java.util.concurrent.Executor} running on a named thread. diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java index 184b83113d8d..051f4335e171 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ThreadFactoryImpl.java @@ -29,10 +29,15 @@ class ThreadFactoryImpl implements ThreadFactory { ThreadFactoryImpl() {} @Override - public Handler builderHandlerOnNewThread(String threadName) { + public Looper buildLooperOnNewThread(String threadName) { HandlerThread handlerThread = new HandlerThread(threadName); handlerThread.start(); - return new Handler(handlerThread.getLooper()); + return handlerThread.getLooper(); + } + + @Override + public Handler buildHandlerOnNewThread(String threadName) { + return new Handler(buildLooperOnNewThread(threadName)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java index b9b7730c67f3..0b2f004537d6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java @@ -116,7 +116,7 @@ public class RotationUtils { default: throw new IllegalArgumentException("Unknown rotation: " + rot); } - Configuration c = context.getResources().getConfiguration(); + Configuration c = new Configuration(context.getResources().getConfiguration()); c.orientation = orientation; Context rotated = context.createConfigurationContext(c); return rotated.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 3aba7ca1ad7c..a5ccc47b8072 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -38,11 +38,9 @@ import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession.Token; import android.net.Uri; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; @@ -66,9 +64,9 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.concurrency.ThreadFactory; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -80,8 +78,6 @@ import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; -import dagger.Lazy; - /** * Source of truth for all state / events related to the volume dialog. No presentation. * @@ -118,12 +114,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call); } - private final HandlerThread mWorkerThread; private final W mWorker; private final Context mContext; + private final Looper mWorkerLooper; + private final PackageManager mPackageManager; + private final WakefulnessLifecycle mWakefulnessLifecycle; private AudioManager mAudio; private IAudioService mAudioService; - private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy; private final NotificationManager mNoMan; private final SettingObserver mObserver; private final Receiver mReceiver = new Receiver(); @@ -132,13 +129,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa protected C mCallbacks = new C(); private final State mState = new State(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks(); - private final Vibrator mVibrator; + private final Optional<Vibrator> mVibrator; private final boolean mHasVibrator; private boolean mShowA11yStream; private boolean mShowVolumeDialog; private boolean mShowSafetyWarning; private long mLastToggledRingerOn; - private final NotificationManager mNotificationManager; + private boolean mDeviceInteractive; private boolean mDestroyed; private VolumePolicy mVolumePolicy; @@ -149,26 +146,42 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa protected final VC mVolumeController = new VC(); protected final BroadcastDispatcher mBroadcastDispatcher; + private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver = + new WakefulnessLifecycle.Observer() { + @Override + public void onStartedWakingUp() { + mDeviceInteractive = true; + } + + @Override + public void onFinishedGoingToSleep() { + mDeviceInteractive = false; + } + }; + @Inject - public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher, - Optional<Lazy<StatusBar>> statusBarOptionalLazy, RingerModeTracker ringerModeTracker) { + public VolumeDialogControllerImpl( + Context context, + BroadcastDispatcher broadcastDispatcher, + RingerModeTracker ringerModeTracker, + ThreadFactory theadFactory, + AudioManager audioManager, + NotificationManager notificationManager, + Optional<Vibrator> optionalVibrator, + IAudioService iAudioService, + AccessibilityManager accessibilityManager, + PackageManager packageManager, + WakefulnessLifecycle wakefulnessLifecycle) { mContext = context.getApplicationContext(); - // TODO(b/150663459): remove this TV workaround once StatusBar is "unbound" on TVs - if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { - mStatusBarOptionalLazy = Optional.empty(); - } else { - mStatusBarOptionalLazy = statusBarOptionalLazy; - } - mNotificationManager = (NotificationManager) mContext.getSystemService( - Context.NOTIFICATION_SERVICE); + mPackageManager = packageManager; + mWakefulnessLifecycle = wakefulnessLifecycle; Events.writeEvent(Events.EVENT_COLLECTION_STARTED); - mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName()); - mWorkerThread.start(); - mWorker = new W(mWorkerThread.getLooper()); - mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(), - mMediaSessionsCallbacksW); - mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mWorkerLooper = theadFactory.buildLooperOnNewThread( + VolumeDialogControllerImpl.class.getSimpleName()); + mWorker = new W(mWorkerLooper); + mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW); + mAudio = audioManager; + mNoMan = notificationManager; mObserver = new SettingObserver(mWorker); mRingerModeObservers = new RingerModeObservers( (RingerModeLiveData) ringerModeTracker.getRingerMode(), @@ -178,16 +191,17 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mBroadcastDispatcher = broadcastDispatcher; mObserver.init(); mReceiver.init(); - mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); - mHasVibrator = mVibrator != null && mVibrator.hasVibrator(); - mAudioService = IAudioService.Stub.asInterface( - ServiceManager.getService(Context.AUDIO_SERVICE)); + mVibrator = optionalVibrator; + mHasVibrator = mVibrator.isPresent() && mVibrator.get().hasVibrator(); + mAudioService = iAudioService; - boolean accessibilityVolumeStreamActive = context.getSystemService( - AccessibilityManager.class).isAccessibilityVolumeStreamActive(); + boolean accessibilityVolumeStreamActive = accessibilityManager + .isAccessibilityVolumeStreamActive(); mVolumeController.setA11yMode(accessibilityVolumeStreamActive ? VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME : VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME); + + mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver); } public AudioManager getAudioManager() { @@ -203,7 +217,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mAudio.setVolumeController(mVolumeController); } catch (SecurityException e) { Log.w(TAG, "Unable to set the volume controller", e); - return; } } @@ -249,18 +262,6 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa return new MediaSessions(context, looper, callbacks); } - public void destroy() { - if (D.BUG) Log.d(TAG, "destroy"); - if (mDestroyed) return; - mDestroyed = true; - Events.writeEvent(Events.EVENT_COLLECTION_STOPPED); - mMediaSessions.destroy(); - mObserver.destroy(); - mReceiver.destroy(); - mRingerModeObservers.destroy(); - mWorkerThread.quitSafely(); - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:"); pw.print(" mDestroyed: "); pw.println(mDestroyed); @@ -383,9 +384,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void vibrate(VibrationEffect effect) { - if (mHasVibrator) { - mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES); - } + mVibrator.ifPresent( + vibrator -> vibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES)); } public boolean hasVibrator() { @@ -437,9 +437,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa return; } - PackageManager packageManager = mContext.getPackageManager(); mCallbacks.onCaptionComponentStateChanged( - packageManager.getComponentEnabledSetting(componentName) + mPackageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED, fromTooltip); } catch (Exception ex) { Log.e(TAG, @@ -466,17 +465,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private boolean shouldShowUI(int flags) { - // if status bar isn't null, check if phone is in AOD, else check flags - // since we could be using a different status bar - return mStatusBarOptionalLazy.map(statusBarLazy -> { - StatusBar statusBar = statusBarLazy.get(); - return statusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP - && statusBar.getWakefulnessState() - != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP - && statusBar.isDeviceInteractive() && (flags & AudioManager.FLAG_SHOW_UI) != 0 - && mShowVolumeDialog; - }).orElse( - mShowVolumeDialog && (flags & AudioManager.FLAG_SHOW_UI) != 0); + int wakefulness = mWakefulnessLifecycle.getWakefulness(); + return wakefulness != WakefulnessLifecycle.WAKEFULNESS_ASLEEP + && wakefulness != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP + && mDeviceInteractive && (flags & AudioManager.FLAG_SHOW_UI) != 0 + && mShowVolumeDialog; } boolean onVolumeChangedW(int stream, int flags) { @@ -600,15 +593,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) { if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false; mState.effectsSuppressor = effectsSuppressor; - mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor); + mState.effectsSuppressorName = + getApplicationName(mPackageManager, mState.effectsSuppressor); Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor, mState.effectsSuppressorName); return true; } - private static String getApplicationName(Context context, ComponentName component) { + private static String getApplicationName(PackageManager pm, ComponentName component) { if (component == null) return null; - final PackageManager pm = context.getPackageManager(); final String pkg = component.getPackageName(); try { final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0); @@ -630,8 +623,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private boolean updateZenConfig() { - final NotificationManager.Policy policy = - mNotificationManager.getConsolidatedNotificationPolicy(); + final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy(); boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy .PRIORITY_CATEGORY_ALARMS) == 0; boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 50b885815e09..961822a598e7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -337,37 +337,44 @@ public class VolumeDialogImpl implements VolumeDialog, mTouchableRegion.setEmpty(); - // Set the touchable region to the union of all child view bounds. We don't use touches on - // the volume dialog container itself, so this is fine. + // Set the touchable region to the union of all child view bounds and the live caption + // tooltip. We don't use touches on the volume dialog container itself, so this is fine. for (int i = 0; i < mDialogView.getChildCount(); i++) { - final View view = mDialogView.getChildAt(i); - final int[] locInWindow = new int[2]; - view.getLocationInWindow(locInWindow); - - float x = locInWindow[0]; - float y = locInWindow[1]; - - // The ringer and rows container has extra height at the top to fit the expanded ringer - // drawer. This area should not be touchable unless the ringer drawer is open. - if (view == mTopContainer && !mIsRingerDrawerOpen) { - if (!isLandscape()) { - y += getRingerDrawerOpenExtraSize(); - } else { - x += getRingerDrawerOpenExtraSize(); - } - } + unionViewBoundstoTouchableRegion(mDialogView.getChildAt(i)); + } - mTouchableRegion.op( - (int) x, - (int) y, - locInWindow[0] + view.getWidth(), - locInWindow[1] + view.getHeight(), - Region.Op.UNION); + if (mODICaptionsTooltipView != null && mODICaptionsTooltipView.getVisibility() == VISIBLE) { + unionViewBoundstoTouchableRegion(mODICaptionsTooltipView); } internalInsetsInfo.touchableRegion.set(mTouchableRegion); } + private void unionViewBoundstoTouchableRegion(final View view) { + final int[] locInWindow = new int[2]; + view.getLocationInWindow(locInWindow); + + float x = locInWindow[0]; + float y = locInWindow[1]; + + // The ringer and rows container has extra height at the top to fit the expanded ringer + // drawer. This area should not be touchable unless the ringer drawer is open. + if (view == mTopContainer && !mIsRingerDrawerOpen) { + if (!isLandscape()) { + y += getRingerDrawerOpenExtraSize(); + } else { + x += getRingerDrawerOpenExtraSize(); + } + } + + mTouchableRegion.op( + (int) x, + (int) y, + locInWindow[0] + view.getWidth(), + locInWindow[1] + view.getHeight(), + Region.Op.UNION); + } + private void initDialog() { mDialog = new CustomDialog(mContext); @@ -1058,21 +1065,38 @@ public class VolumeDialogImpl implements VolumeDialog, } if (mODICaptionsTooltipView != null) { - mODICaptionsTooltipView.setAlpha(0.f); - mODICaptionsTooltipView.animate() - .alpha(1.f) - .setStartDelay(mDialogShowAnimationDurationMs) - .withEndAction(() -> { - if (D.BUG) Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true"); - Prefs.putBoolean(mContext, - Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true); - mHasSeenODICaptionsTooltip = true; - if (mODICaptionsIcon != null) { - mODICaptionsIcon - .postOnAnimation(getSinglePressFor(mODICaptionsIcon)); - } - }) - .start(); + mODICaptionsTooltipView.setAlpha(0.0f); + + // We need to wait for layout and then center the caption view. Since the height of the + // dialog is now dynamic (with the variable ringer drawer height changing the height of + // the dialog), we need to do this here in code vs. in XML. + mHandler.post(() -> { + final int[] odiTooltipLocation = mODICaptionsTooltipView.getLocationOnScreen(); + final int[] odiButtonLocation = mODICaptionsIcon.getLocationOnScreen(); + + final float heightDiffForCentering = + (mODICaptionsTooltipView.getHeight() - mODICaptionsIcon.getHeight()) / 2f; + + mODICaptionsTooltipView.setTranslationY( + odiButtonLocation[1] - odiTooltipLocation[1] - heightDiffForCentering); + + mODICaptionsTooltipView.animate() + .alpha(1.0f) + .setStartDelay(mDialogShowAnimationDurationMs) + .withEndAction(() -> { + if (D.BUG) { + Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true"); + } + Prefs.putBoolean(mContext, + Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true); + mHasSeenODICaptionsTooltip = true; + if (mODICaptionsIcon != null) { + mODICaptionsIcon + .postOnAnimation(getSinglePressFor(mODICaptionsIcon)); + } + }) + .start(); + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java new file mode 100644 index 000000000000..9d0cc6a00ec0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallet.controller; + +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; +import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; + +import android.content.Context; +import android.database.ContentObserver; +import android.provider.Settings; +import android.service.quickaccesswallet.GetWalletCardsRequest; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.service.quickaccesswallet.QuickAccessWalletClientImpl; +import android.util.Log; + +import com.android.systemui.R; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.settings.SecureSettings; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * Controller to handle communication between SystemUI and Quick Access Wallet Client. + */ +@SysUISingleton +public class QuickAccessWalletController { + + /** + * Event for the wallet status change, e.g. the default payment app change and the wallet + * preference change. + */ + public enum WalletChangeEvent { + DEFAULT_PAYMENT_APP_CHANGE, + WALLET_PREFERENCE_CHANGE, + } + + private static final String TAG = "QAWController"; + private final Context mContext; + private final Executor mExecutor; + private final SecureSettings mSecureSettings; + + private QuickAccessWalletClient mQuickAccessWalletClient; + private ContentObserver mWalletPreferenceObserver; + private ContentObserver mDefaultPaymentAppObserver; + private int mWalletPreferenceChangeEvents = 0; + private int mDefaultPaymentAppChangeEvents = 0; + private boolean mWalletEnabled = false; + + @Inject + public QuickAccessWalletController( + Context context, + @Main Executor executor, + SecureSettings secureSettings, + QuickAccessWalletClient quickAccessWalletClient) { + mContext = context; + mExecutor = executor; + mSecureSettings = secureSettings; + mQuickAccessWalletClient = quickAccessWalletClient; + } + + /** + * Returns true if the Quick Access Wallet service & feature is available. + */ + public boolean isWalletEnabled() { + return mWalletEnabled; + } + + /** + * Returns the current instance of {@link QuickAccessWalletClient} in the controller. + */ + public QuickAccessWalletClient getWalletClient() { + return mQuickAccessWalletClient; + } + + /** + * Setup the wallet change observers per {@link WalletChangeEvent} + * + * @param cardsRetriever a callback that retrieves the wallet cards + * @param events {@link WalletChangeEvent} need to be handled. + */ + public void setupWalletChangeObservers( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever, + WalletChangeEvent... events) { + for (WalletChangeEvent event : events) { + if (event == WALLET_PREFERENCE_CHANGE) { + setupWalletPreferenceObserver(); + } else if (event == DEFAULT_PAYMENT_APP_CHANGE) { + setupDefaultPaymentAppObserver(cardsRetriever); + } + } + } + + /** + * Unregister wallet change observers per {@link WalletChangeEvent} if needed. + * + */ + public void unregisterWalletChangeObservers(WalletChangeEvent... events) { + for (WalletChangeEvent event : events) { + if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) { + mWalletPreferenceChangeEvents--; + if (mWalletPreferenceChangeEvents == 0) { + mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver); + } + } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) { + mDefaultPaymentAppChangeEvents--; + if (mDefaultPaymentAppChangeEvents == 0) { + mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver); + } + } + } + } + + /** + * Update the "show wallet" preference. + */ + public void updateWalletPreference() { + mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable() + && mQuickAccessWalletClient.isWalletFeatureAvailable() + && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked(); + } + + /** + * Query the wallet cards from {@link QuickAccessWalletClient}. + * + * @param cardsRetriever a callback to retrieve wallet cards. + */ + public void queryWalletCards( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (!mWalletEnabled) { + Log.w(TAG, "QuickAccessWallet is unavailable, unable to query cards."); + return; + } + int cardWidth = + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); + int cardHeight = + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); + int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); + GetWalletCardsRequest request = + new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); + mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever); + } + + /** + * Re-create the {@link QuickAccessWalletClient} of the controller. + */ + public void reCreateWalletClient() { + mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext); + } + + private void setupDefaultPaymentAppObserver( + QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (mDefaultPaymentAppObserver == null) { + mDefaultPaymentAppObserver = new ContentObserver(null /* handler */) { + @Override + public void onChange(boolean selfChange) { + mExecutor.execute(() -> { + reCreateWalletClient(); + updateWalletPreference(); + queryWalletCards(cardsRetriever); + }); + } + }; + + mSecureSettings.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT), + false /* notifyForDescendants */, + mDefaultPaymentAppObserver); + } + mDefaultPaymentAppChangeEvents++; + } + + private void setupWalletPreferenceObserver() { + if (mWalletPreferenceObserver == null) { + mWalletPreferenceObserver = new ContentObserver(null /* handler */) { + @Override + public void onChange(boolean selfChange) { + mExecutor.execute(() -> { + updateWalletPreference(); + }); + } + }; + + mSecureSettings.registerContentObserver( + Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY), + false /* notifyForDescendants */, + mWalletPreferenceObserver); + } + mWalletPreferenceChangeEvents++; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 83aa01f8d393..c6123e77076d 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.Window; @@ -52,7 +53,7 @@ import javax.inject.Inject; */ public class WalletActivity extends LifecycleActivity { - private final QuickAccessWalletClient mQuickAccessWalletClient; + private static final String TAG = "WalletActivity"; private final KeyguardStateController mKeyguardStateController; private final KeyguardDismissUtil mKeyguardDismissUtil; private final ActivityStarter mActivityStarter; @@ -65,7 +66,6 @@ public class WalletActivity extends LifecycleActivity { @Inject public WalletActivity( - QuickAccessWalletClient quickAccessWalletClient, KeyguardStateController keyguardStateController, KeyguardDismissUtil keyguardDismissUtil, ActivityStarter activityStarter, @@ -74,7 +74,6 @@ public class WalletActivity extends LifecycleActivity { FalsingManager falsingManager, UserTracker userTracker, StatusBarKeyguardViewManager keyguardViewManager) { - mQuickAccessWalletClient = quickAccessWalletClient; mKeyguardStateController = keyguardStateController; mKeyguardDismissUtil = keyguardDismissUtil; mActivityStarter = activityStarter; @@ -103,10 +102,11 @@ public class WalletActivity extends LifecycleActivity { getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close); WalletView walletView = requireViewById(R.id.wallet_view); + QuickAccessWalletClient walletClient = QuickAccessWalletClient.create(this); mWalletScreenController = new WalletScreenController( this, walletView, - mQuickAccessWalletClient, + walletClient, mActivityStarter, mExecutor, mHandler, @@ -116,6 +116,10 @@ public class WalletActivity extends LifecycleActivity { walletView.getAppButton().setOnClickListener( v -> { + if (walletClient.createWalletIntent() == null) { + Log.w(TAG, "Unable to create wallet app intent."); + return; + } if (!mKeyguardStateController.isUnlocked() && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; @@ -123,12 +127,12 @@ public class WalletActivity extends LifecycleActivity { if (mKeyguardStateController.isUnlocked()) { mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true); + walletClient.createWalletIntent(), true); finish(); } else { mKeyguardDismissUtil.executeWhenUnlocked(() -> { mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true); + walletClient.createWalletIntent(), true); finish(); return false; }, false, true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 10322f287d50..06b0bb25e01c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -40,8 +40,10 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -86,6 +88,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock Resources mResources; + KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock + SmartspaceTransitionController mSmartSpaceTransitionController; @Mock private ClockPlugin mClockPlugin; @Mock @@ -135,7 +140,10 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mBatteryController, mKeyguardUpdateMonitor, mBypassController, - mSmartspaceController); + mSmartspaceController, + mKeyguardUnlockAnimationController, + mSmartSpaceTransitionController + ); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 49f1655997ef..f9b6d4467e3c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -22,6 +22,8 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -50,6 +52,10 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { ConfigurationController mConfigurationController; @Mock DozeParameters mDozeParameters; + @Mock + KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock + SmartspaceTransitionController mSmartSpaceTransitionController; private KeyguardStatusViewController mController; @@ -64,7 +70,9 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { mKeyguardStateController, mKeyguardUpdateMonitor, mConfigurationController, - mDozeParameters); + mDozeParameters, + mKeyguardUnlockAnimationController, + mSmartSpaceTransitionController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index c6aef4a18373..bf87a4a59c49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -20,6 +20,7 @@ import android.graphics.Rect import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.ViewGroup +import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq @@ -33,6 +34,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView +import junit.framework.Assert import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Rule @@ -65,8 +67,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Mock private lateinit var bypassController: KeyguardBypassController @Mock - private lateinit var mediaFrame: ViewGroup - @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @@ -90,9 +90,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Rule val mockito = MockitoJUnit.rule() private lateinit var mediaHiearchyManager: MediaHierarchyManager + private lateinit var mediaFrame: ViewGroup @Before fun setup() { + mediaFrame = FrameLayout(context) `when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame) mediaHiearchyManager = MediaHierarchyManager( context, @@ -112,6 +114,9 @@ class MediaHierarchyManagerTest : SysuiTestCase() { `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE) `when`(mediaCarouselController.mediaCarouselScrollHandler) .thenReturn(mediaCarouselScrollHandler) + val observer = wakefullnessObserver.value + assertNotNull("lifecycle observer wasn't registered", observer) + observer.onFinishedWakingUp() // We'll use the viewmanager to verify a few calls below, let's reset this. clearInvocations(mediaCarouselController) } @@ -120,6 +125,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { `when`(host.location).thenReturn(location) `when`(host.currentBounds).thenReturn(Rect()) `when`(host.hostView).thenReturn(UniqueObjectHostView(context)) + `when`(host.visible).thenReturn(true) mediaHiearchyManager.register(host) } @@ -160,6 +166,73 @@ class MediaHierarchyManagerTest : SysuiTestCase() { } @Test + fun testGoingToFullShade() { + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + + // Let's transition all the way to full shade + mediaHiearchyManager.setTransitionToFullShadeAmount(100000f) + verify(mediaCarouselController).onDesiredLocationChanged( + eq(MediaHierarchyManager.LOCATION_QQS), + any(MediaHostState::class.java), + eq(false), + anyLong(), + anyLong()) + clearInvocations(mediaCarouselController) + + // Let's go back to the lock screen + mediaHiearchyManager.setTransitionToFullShadeAmount(0.0f) + verify(mediaCarouselController).onDesiredLocationChanged( + eq(MediaHierarchyManager.LOCATION_LOCKSCREEN), + any(MediaHostState::class.java), + eq(false), + anyLong(), + anyLong()) + + // Let's make sure alpha is set + mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f) + Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f) + } + + @Test + fun testTransformationOnLockScreenIsFading() { + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + + // Let's transition from lockscreen to qs + mediaHiearchyManager.qsExpansion = 1.0f + val transformType = mediaHiearchyManager.calculateTransformationType() + Assert.assertTrue("media isn't transforming to qs with a fade", + transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + } + + @Test + fun testTransformationOnLockScreenToQQSisFading() { + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + + // Let's transition from lockscreen to qs + `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD, + StatusBarState.SHADE_LOCKED) + val transformType = mediaHiearchyManager.calculateTransformationType() + Assert.assertTrue("media isn't transforming to qqswith a fade", + transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + } + + @Test fun testCloseGutsRelayToCarousel() { mediaHiearchyManager.closeGuts() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt index 96d1d94795f6..4e1627ff0343 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt @@ -89,6 +89,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var dumpManager: DumpManager @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> + @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable> private lateinit var executor: FakeExecutor private lateinit var data: MediaData @@ -224,9 +225,6 @@ class MediaResumeListenerTest : SysuiTestCase() { // But we do not tell it to add new controls verify(mediaDataManager, never()) .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any()) - - // Finally, make sure the resume browser disconnected - verify(resumeBrowser).disconnect() } @Test @@ -267,4 +265,39 @@ class MediaResumeListenerTest : SysuiTestCase() { verify(mediaDataManager, times(3)).addResumptionControls(anyInt(), any(), any(), any(), any(), any(), eq(PACKAGE_NAME)) } + + @Test + fun testGetResumeAction_restarts() { + // Set up mocks to successfully find a MBS that returns valid media + val pm = mock(PackageManager::class.java) + whenever(mockContext.packageManager).thenReturn(pm) + val resolveInfo = ResolveInfo() + val serviceInfo = ServiceInfo() + serviceInfo.packageName = PACKAGE_NAME + resolveInfo.serviceInfo = serviceInfo + resolveInfo.serviceInfo.name = CLASS_NAME + val resumeInfo = listOf(resolveInfo) + whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo) + + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.testConnection()).thenAnswer { + callbackCaptor.value.addTrack(description, component, resumeBrowser) + } + + // When media data is loaded that has not been checked yet, and does have a MBS + val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false) + resumeListener.onMediaDataLoaded(KEY, null, dataCopy) + + // Then we test whether the service is valid and set the resume action + executor.runAllReady() + verify(resumeBrowser).testConnection() + verify(mediaDataManager).setResumeAction(eq(KEY), capture(actionCaptor)) + + // When the resume action is run + actionCaptor.value.run() + + // Then we call restart + verify(resumeBrowser).restart() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt index d26229edf71a..dfa7c66b38f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt @@ -91,8 +91,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.testConnection() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -111,8 +112,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNoResults() resumeBrowser.testConnection() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -132,8 +134,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -143,8 +146,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { whenever(browser.getRoot()).thenReturn(null) resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -163,8 +167,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNoResults() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -173,8 +178,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserConnectionNotPlayable() resumeBrowser.findRecentMedia() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -193,8 +199,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { setupBrowserFailed() resumeBrowser.restart() - // Then it calls onError + // Then it calls onError and disconnects verify(callback).onError() + verify(browser).disconnect() } @Test @@ -202,13 +209,11 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { // When restart is called and we connect successfully setupBrowserConnection() resumeBrowser.restart() + verify(callback).onConnected() // Then it creates a new controller and sends play command verify(transportControls).prepare() verify(transportControls).play() - - // Then it calls onConnected - verify(callback).onConnected() } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 7533cf1310de..b09afab3d242 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -73,6 +73,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.google.common.util.concurrent.MoreExecutors; @@ -119,6 +120,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock + private QuickAccessWalletController mController; + @Mock private FeatureFlags mFeatureFlags; @Captor ArgumentCaptor<Intent> mIntentCaptor; @@ -145,6 +148,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL); when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true); + when(mController.getWalletClient()).thenReturn(mQuickAccessWalletClient); mTile = new QuickAccessWalletTile( mHost, @@ -155,11 +160,11 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mStatusBarStateController, mActivityStarter, mQSLogger, - mQuickAccessWalletClient, mKeyguardStateController, mPackageManager, mSecureSettings, MoreExecutors.directExecutor(), + mController, mFeatureFlags); } @@ -175,6 +180,15 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testWalletServiceUnavailable_recreateWalletClient() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mTile.handleSetListening(true); + + verify(mController, times(1)).reCreateWalletClient(); + } + + @Test public void testIsAvailable_qawFeatureAvailable() { when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true); when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); @@ -330,17 +344,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { public void testHandleSetListening_queryCards() { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient) - .getWalletCards(any(), mRequestCaptor.capture(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); - GetWalletCardsRequest request = mRequestCaptor.getValue(); - assertEquals( - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width), - request.getCardWidthPx()); - assertEquals( - mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), - request.getCardHeightPx()); - assertEquals(1, request.getMaxCards()); assertThat(mCallbackCaptor.getValue()).isInstanceOf( QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class); } @@ -354,37 +359,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test - public void testState_queryCards_hasCards_then_noCards() { - when(mKeyguardStateController.isUnlocked()).thenReturn(true); - GetWalletCardsResponse responseWithCards = - new GetWalletCardsResponse( - Collections.singletonList(createWalletCard(mContext)), 0); - GetWalletCardsResponse responseWithoutCards = - new GetWalletCardsResponse(Collections.EMPTY_LIST, 0); - - mTile.handleSetListening(true); - - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); - - // query wallet cards, has cards - mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards); - mTestableLooper.processAllMessages(); - - assertNotNull(mTile.getState().sideViewCustomDrawable); - - mTile.handleSetListening(true); - - verify(mQuickAccessWalletClient, times(2)) - .getWalletCards(any(), any(), mCallbackCaptor.capture()); - - // query wallet cards, has no cards - mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards); - mTestableLooper.processAllMessages(); - - assertNull(mTile.getState().sideViewCustomDrawable); - } - - @Test public void testQueryCards_noCards_notUpdateSideViewDrawable() { setUpWalletCard(/* hasCard= */ false); @@ -398,7 +372,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); mCallbackCaptor.getValue().onWalletCardRetrievalError(error); mTestableLooper.processAllMessages(); @@ -422,7 +396,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleSetListening(true); - verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + verify(mController).queryWalletCards(mCallbackCaptor.capture()); mCallbackCaptor.getValue().onWalletCardsRetrieved(response); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt index 9b5c33d64eb9..116f807a888d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -511,6 +511,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() { override fun setNextAlarm(image: Drawable?, description: String?) { } + + override fun setMediaTarget(target: SmartspaceTarget?) { + } }) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 6b4797fc5723..ee8d1209a5cb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -20,12 +20,15 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; +import static com.android.systemui.statusbar.notification.ViewGroupFadeHelper.reset; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -39,7 +42,6 @@ import android.content.res.Resources; import android.hardware.biometrics.BiometricSourceType; import android.os.PowerManager; import android.os.UserManager; -import android.service.quickaccesswallet.QuickAccessWalletClient; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.DisplayMetrics; @@ -75,13 +77,17 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardAffordanceView; +import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -105,6 +111,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; import org.junit.Before; @@ -245,18 +252,28 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private LockIconViewController mLockIconViewController; @Mock - private QuickAccessWalletClient mQuickAccessWalletClient; - @Mock private KeyguardMediaController mKeyguardMediaController; @Mock private PrivacyDotViewController mPrivacyDotViewController; @Mock private SecureSettings mSecureSettings; + @Mock + private TapAgainViewController mTapAgainViewController; + @Mock + private KeyguardIndicationController mKeyguardIndicationController; + @Mock + private FragmentService mFragmentService; + @Mock + private FragmentHostManager mFragmentHostManager; + @Mock + private QuickAccessWalletController mQuickAccessWalletController; private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; private NotificationsQuickSettingsContainer mNotificationContainerParent; + private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; + private FalsingManagerFake mFalsingManager = new FalsingManagerFake(); @Before public void setup() { @@ -297,6 +314,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view)); when(mView.findViewById(R.id.notification_container_parent)) .thenReturn(mNotificationContainerParent); + when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( mDisplayMetrics); @@ -317,7 +335,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardBypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class), mStatusBarStateController, - new FalsingManagerFake(), + mFalsingManager, mLockscreenShadeTransitionController, new FalsingCollectorFake()); when(mKeyguardStatusViewComponentFactory.build(any())) @@ -331,11 +349,12 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) .thenReturn(mKeyguardStatusBarViewController); + reset(mView); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, mLayoutInflater, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - new FalsingManagerFake(), new FalsingCollectorFake(), + mFalsingManager, new FalsingCollectorFake(), mNotificationLockscreenUserManager, mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, @@ -361,9 +380,11 @@ public class NotificationPanelViewTest extends SysuiTestCase { mAmbientState, mLockIconViewController, mFeatureFlags, - mQuickAccessWalletClient, mKeyguardMediaController, mPrivacyDotViewController, + mTapAgainViewController, + mFragmentService, + mQuickAccessWalletController, new FakeExecutor(new FakeSystemClock()), mSecureSettings); mNotificationPanelViewController.initDependencies( @@ -371,6 +392,13 @@ public class NotificationPanelViewTest extends SysuiTestCase { mNotificationShelfController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mNotificationPanelViewController.setBar(mPanelBar); + mNotificationPanelViewController.setKeyguardIndicationController( + mKeyguardIndicationController); + ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + verify(mView, atLeast(1)).addOnAttachStateChangeListener( + onAttachStateChangeListenerArgumentCaptor.capture()); + mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = ArgumentCaptor.forClass(View.AccessibilityDelegate.class); @@ -616,6 +644,34 @@ public class NotificationPanelViewTest extends SysuiTestCase { verify(mKeyguardStateController).notifyPanelFlingEnd(); } + @Test + public void testDoubleTapRequired_Keyguard() { + FalsingManager.FalsingTapListener listener = getFalsingTapListener(); + mStatusBarStateController.setState(KEYGUARD); + + listener.onDoubleTapRequired(); + + verify(mKeyguardIndicationController).showTransientIndication(anyInt()); + } + + @Test + public void testDoubleTapRequired_ShadeLocked() { + FalsingManager.FalsingTapListener listener = getFalsingTapListener(); + mStatusBarStateController.setState(SHADE_LOCKED); + + listener.onDoubleTapRequired(); + + verify(mTapAgainViewController).show(); + } + + private FalsingManager.FalsingTapListener getFalsingTapListener() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); + return mFalsingManager.getTapListeners().get(0); + } + private View newViewWithId(int id) { View view = new View(mContext); view.setId(id); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt new file mode 100644 index 000000000000..4796cd768980 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.graphics.Rect +import android.test.suitebuilder.annotation.SmallTest +import android.view.DisplayCutout +import android.view.WindowMetrics +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.leak.RotationUtils +import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE +import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE +import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN +import com.android.systemui.util.leak.RotationUtils.Rotation +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +class StatusBarContentInsetsProviderTest : SysuiTestCase() { + @Mock private lateinit var dc: DisplayCutout + @Mock private lateinit var windowMetrics: WindowMetrics + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testGetBoundingRectForPrivacyChipForRotation_noCutout() { + val screenBounds = Rect(0, 0, 1080, 2160) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + val chipWidth = 30 + val dotWidth = 10 + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + + var isRtl = false + var targetRotation = ROTATION_NONE + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 1080 - 20 (rounded corner) - 30 (chip), + * 0 (sb top) + * 1080 - 20 (rounded corner) + 10 ( dot), + * 100 (sb height portrait) + */ + var expected = Rect(1030, 0, 1070, 100) + assertRects(expected, chipBounds, currentRotation, targetRotation) + isRtl = true + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 0 + 20 (rounded corner) - 10 (dot), + * 0 (sb top) + * 0 + 20 (rounded corner) + 30 (chip), + * 100 (sb height portrait) + */ + expected = Rect(10, 0, 50, 100) + assertRects(expected, chipBounds, currentRotation, targetRotation) + + isRtl = false + targetRotation = ROTATION_LANDSCAPE + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 2160 - 20 (rounded corner) - 30 (chip), + * 0 (sb top) + * 2160 - 20 (rounded corner) + 10 ( dot), + * 60 (sb height landscape) + */ + expected = Rect(2110, 0, 2150, 60) + assertRects(expected, chipBounds, currentRotation, targetRotation) + isRtl = true + chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl) + /* 0 + 20 (rounded corner) - 10 (dot), + * 0 (sb top) + * 0 + 20 (rounded corner) + 30 (chip), + * 60 (sb height landscape) + */ + expected = Rect(10, 0, 50, 60) + assertRects(expected, chipBounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() { + // GIVEN a device in portrait mode with width < height and a display cutout in the top-left + val screenBounds = Rect(0, 0, 1080, 2160) + val dcBounds = Rect(0, 0, 100, 100) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + `when`(dc.boundingRects).thenReturn(listOf(dcBounds)) + + // THEN rotations which share a short side should use the greater value between rounded + // corner padding and the display cutout's size + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(dcBounds.right, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(dcBounds.height(), + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + // THEN the side that does NOT share a short side with the display cutout ignores the + // display cutout bounds + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.width() - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + // Phone in portrait, seascape (rot_270) bounds + targetRotation = ROTATION_SEASCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - dcBounds.height(), + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() { + // GIVEN phone in portrait mode, where width < height and the cutout is not in the corner + // the assumption here is that if the cutout does NOT touch the corner then we have room to + // layout the status bar in the given space. + + val screenBounds = Rect(0, 0, 1080, 2160) + // cutout centered at the top + val dcBounds = Rect(490, 0, 590, 100) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + val currentRotation = ROTATION_NONE + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + `when`(dc.boundingRects).thenReturn(listOf(dcBounds)) + + // THEN only the landscape/seascape rotations should avoid the cutout area because of the + // potential letterboxing + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(dcBounds.height(), + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_SEASCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - dcBounds.height(), + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + dc, + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + @Test + fun testCalculateInsetsForRotationWithRotatedResources_noCutout() { + // GIVEN device in portrait mode, where width < height and no cutout + val currentRotation = ROTATION_NONE + val screenBounds = Rect(0, 0, 1080, 2160) + val roundedCornerPadding = 20 + val sbHeightPortrait = 100 + val sbHeightLandscape = 60 + + `when`(windowMetrics.bounds).thenReturn(screenBounds) + + // THEN content insets should only use rounded corner padding + var targetRotation = ROTATION_NONE + var expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.right - roundedCornerPadding, + sbHeightPortrait) + + var bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_UPSIDE_DOWN + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.width() - roundedCornerPadding, + sbHeightPortrait) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightPortrait, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + + targetRotation = ROTATION_LANDSCAPE + expectedBounds = Rect(roundedCornerPadding, + 0, + screenBounds.height() - roundedCornerPadding, + sbHeightLandscape) + + bounds = calculateInsetsForRotationWithRotatedResources( + currentRotation, + targetRotation, + null, /* no cutout */ + windowMetrics, + sbHeightLandscape, + roundedCornerPadding) + assertRects(expectedBounds, bounds, currentRotation, targetRotation) + } + + private fun assertRects( + expected: Rect, + actual: Rect, + @Rotation currentRotation: Int, + @Rotation targetRotation: Int + ) { + assertTrue( + "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + + " targetRotation=${RotationUtils.toString(targetRotation)}" + + " expected=$expected actual=$actual", + expected.equals(actual)) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java index 53a2efce9bb8..4b87ec895773 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java @@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import org.junit.Before; import org.junit.Test; @@ -49,12 +50,14 @@ public class KeyguardStateControllerTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; private KeyguardStateController mKeyguardStateController; + @Mock + private SmartspaceTransitionController mSmartSpaceTransitionController; @Before public void setup() { MockitoAnnotations.initMocks(this); mKeyguardStateController = new KeyguardStateControllerImpl(mContext, - mKeyguardUpdateMonitor, mLockPatternUtils); + mKeyguardUpdateMonitor, mLockPatternUtils, mSmartSpaceTransitionController); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 365c62cddbdf..9b177e1cd0e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -17,7 +17,6 @@ package com.android.systemui.toast; import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; -import static android.widget.ToastPresenter.TEXT_TOAST_LAYOUT; import static com.google.common.truth.Truth.assertThat; @@ -31,13 +30,20 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Application; import android.app.INotificationManager; import android.app.ITransientNotificationCallback; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Binder; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -47,12 +53,11 @@ import android.view.accessibility.IAccessibilityManager; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; -import android.widget.ToastPresenter; import androidx.test.filters.SmallTest; -import com.android.internal.R; import com.android.internal.util.IntPair; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.plugins.PluginManager; @@ -70,6 +75,7 @@ import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class ToastUITest extends SysuiTestCase { private static final int ANDROID_UID = 1000; private static final int SYSTEMUI_UID = 10140; @@ -85,12 +91,14 @@ public class ToastUITest extends SysuiTestCase { private static final Binder WINDOW_TOKEN_2 = new Binder(); private static final String TEXT = "Hello World"; - private static final int MESSAGE_RES_ID = R.id.message; + private static final int MESSAGE_RES_ID = R.id.text; private Context mContextSpy; private ToastUI mToastUI; - @Mock private LayoutInflater mLayoutInflater; + private View mToastView; + @Mock private Application mApplication; @Mock private CommandQueue mCommandQueue; + @Mock private LayoutInflater mLayoutInflater; @Mock private WindowManager mWindowManager; @Mock private INotificationManager mNotificationManager; @Mock private IAccessibilityManager mAccessibilityManager; @@ -98,6 +106,7 @@ public class ToastUITest extends SysuiTestCase { @Mock private DumpManager mDumpManager; @Mock private ToastLogger mToastLogger; @Mock private FeatureFlags mFeatureFlags; + @Mock private PackageManager mPackageManager; @Mock private ITransientNotificationCallback mCallback; @Captor private ArgumentCaptor<View> mViewCaptor; @@ -106,29 +115,33 @@ public class ToastUITest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn( - ToastPresenter.getTextToastView(mContext, TEXT)); - when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false); - + mToastView = LayoutInflater.from(mContext).inflate(R.layout.text_toast, null); + when(mLayoutInflater.inflate(anyInt(), eq(null))).thenReturn(mToastView); mContext.addMockSystemService(WindowManager.class, mWindowManager); mContextSpy = spy(mContext); + when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); - - doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); - mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager, - mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager, - mDumpManager, mFeatureFlags), mToastLogger); + mToastUI = new ToastUI( + mContextSpy, + mCommandQueue, + mNotificationManager, + mAccessibilityManager, + new ToastFactory( + mLayoutInflater, + mPluginManager, + mDumpManager), + mToastLogger); } @Test - public void testStart_addToastUIAsCallbackToCommandQueue() throws Exception { + public void testStart_addToastUIAsCallbackToCommandQueue() { mToastUI.start(); verify(mCommandQueue).addCallback(mToastUI); } @Test - public void testShowToast_addsCorrectViewToWindowManager() throws Exception { + public void testShowToast_addsCorrectViewToWindowManager() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); @@ -138,7 +151,7 @@ public class ToastUITest extends SysuiTestCase { } @Test - public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() throws Exception { + public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); @@ -217,9 +230,14 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_removesView() throws Exception { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); - View view = verifyWmAddViewAndAttachToParent(); + final SystemUIToast toast = mToastUI.mToast; + View view = verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // if applicable, try to finish anim early + } verify(mWindowManager).removeViewImmediate(view); } @@ -228,51 +246,81 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_finishesToken() throws Exception { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // if applicable, try to finish anim early + } verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1); } @Test - public void testHideToast_callsCallback() throws Exception { + public void testHideToast_callsCallback() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); + } verify(mCallback).onToastHidden(); } @Test - public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws Exception { + public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_2); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isFalse(); + } + verify(mCallback, never()).onToastHidden(); } @Test - public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws Exception { + public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + final SystemUIToast toast = mToastUI.mToast; + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_2, TOKEN_1); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isFalse(); + } + verify(mCallback, never()).onToastHidden(); } @Test - public void testShowToast_afterShowToast_hidesCurrentToast() throws Exception { + public void testShowToast_afterShowToast_hidesCurrentToast() throws RemoteException { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); - View view = verifyWmAddViewAndAttachToParent(); + final SystemUIToast toast = mToastUI.mToast; + View view = verifyWmAddViewAndAttachToParent(); mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG, null); + if (toast.getOutAnimation() != null) { + assertThat(toast.getOutAnimation().isRunning()).isTrue(); + toast.getOutAnimation().cancel(); // end early if applicable + } + verify(mWindowManager).removeViewImmediate(view); verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1); verify(mCallback).onToastHidden(); @@ -287,9 +335,48 @@ public class ToastUITest extends SysuiTestCase { } @Test + public void testShowToast_targetsPreS_unlimitedLines_noAppIcon() + throws PackageManager.NameNotFoundException { + // GIVEN the application targets R + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; + when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0, + UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo); + + // WHEN the package posts a toast + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback); + + // THEN the view can have unlimited lines + assertThat(((TextView) mToastUI.mToast.getView() + .findViewById(com.android.systemui.R.id.text)) + .getMaxLines()).isEqualTo(Integer.MAX_VALUE); + } + + @Test + public void testShowToast_targetsS_twoLineLimit_noAppIcon() + throws PackageManager.NameNotFoundException { + // GIVEN the application targets S + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.targetSdkVersion = Build.VERSION_CODES.S; + when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0, + UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo); + + // WHEN the package posts a toast + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback); + + // THEN the view is limited to 2 lines + assertThat(((TextView) mToastUI.mToast.getView() + .findViewById(com.android.systemui.R.id.text)) + .getMaxLines()).isEqualTo(2); + } + + @Test public void testHideToast_logs() { mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); + verifyWmAddViewAndAttachToParent(); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); verify(mToastLogger).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } @@ -298,6 +385,7 @@ public class ToastUITest extends SysuiTestCase { public void testHideToast_error_noLog() { // no toast was shown, so this hide is invalid mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); + assertThat(mToastUI.mToast).isNull(); verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java index 570e1d86574e..301a157bd42e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeThreadFactory.java @@ -27,6 +27,7 @@ import java.util.concurrent.Executor; public class FakeThreadFactory implements ThreadFactory { private final FakeExecutor mFakeExecutor; private Handler mHandler; + private Looper mLooper; public FakeThreadFactory(FakeExecutor fakeExecutor) { mFakeExecutor = fakeExecutor; @@ -36,8 +37,17 @@ public class FakeThreadFactory implements ThreadFactory { mHandler = handler; } + public void setLooper(Looper looper) { + mLooper = looper; + } + + @Override + public Looper buildLooperOnNewThread(String threadName) { + return mLooper; + } + @Override - public Handler builderHandlerOnNewThread(String threadName) { + public Handler buildHandlerOnNewThread(String threadName) { return mHandler; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java index 5fb779adc5be..1aebf1c1c80d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java @@ -127,4 +127,9 @@ public class FakeKeyguardStateController implements KeyguardStateController { public void notifyPanelFlingEnd() { } + + @Override + public boolean canPerformSmartSpaceTransition() { + return false; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index 6166cd76e005..5c0efd36fcd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -23,29 +23,37 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.media.AudioManager; +import android.media.IAudioService; import android.media.session.MediaSession; import android.os.Handler; import android.os.Process; +import android.os.Vibrator; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.RingerModeLiveData; import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.concurrency.FakeThreadFactory; +import com.android.systemui.util.concurrency.ThreadFactory; +import com.android.systemui.util.time.FakeSystemClock; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -58,7 +66,6 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { TestableVolumeDialogControllerImpl mVolumeController; VolumeDialogControllerImpl.C mCallback; - StatusBar mStatusBar; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock @@ -67,6 +74,23 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { private RingerModeLiveData mRingerModeLiveData; @Mock private RingerModeLiveData mRingerModeInternalLiveData; + private final FakeThreadFactory mThreadFactory = new FakeThreadFactory( + new FakeExecutor(new FakeSystemClock())); + @Mock + private AudioManager mAudioManager; + @Mock + private NotificationManager mNotificationManager; + @Mock + private Vibrator mVibrator; + @Mock + private IAudioService mIAudioService; + @Mock + private AccessibilityManager mAccessibilityManager; + @Mock + private PackageManager mPackageManager; + @Mock + private WakefulnessLifecycle mWakefullnessLifcycle; + @Before public void setup() throws Exception { @@ -77,19 +101,15 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { // Initial non-set value when(mRingerModeLiveData.getValue()).thenReturn(-1); when(mRingerModeInternalLiveData.getValue()).thenReturn(-1); - mCallback = mock(VolumeDialogControllerImpl.C.class); - mStatusBar = mock(StatusBar.class); - mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar, - mBroadcastDispatcher, mRingerModeTracker); + mThreadFactory.setLooper(TestableLooper.get(this).getLooper()); + mVolumeController = new TestableVolumeDialogControllerImpl(mContext, + mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, + mNotificationManager, Optional.of(mVibrator), mIAudioService, mAccessibilityManager, + mPackageManager, mWakefullnessLifcycle, mCallback); mVolumeController.setEnableDialogs(true, true); } - @After - public void tearDown() { - mVolumeController.destroy(); - } - @Test public void testRegisteredWithDispatcher() { verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class), @@ -99,45 +119,36 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { @Test public void testVolumeChangeW_deviceNotInteractiveAOD() { - when(mStatusBar.isDeviceInteractive()).thenReturn(false); - when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + mVolumeController.setDeviceInteractive(false); + when(mWakefullnessLifcycle.getWakefulness()).thenReturn( + WakefulnessLifecycle.WAKEFULNESS_AWAKE); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); } @Test public void testVolumeChangeW_deviceInteractive() { - when(mStatusBar.isDeviceInteractive()).thenReturn(true); - when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + mVolumeController.setDeviceInteractive(true); + when(mWakefullnessLifcycle.getWakefulness()).thenReturn( + WakefulnessLifecycle.WAKEFULNESS_AWAKE); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); } @Test public void testVolumeChangeW_deviceInteractive_StartedSleeping() { - when(mStatusBar.isDeviceInteractive()).thenReturn(true); - when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE); + mVolumeController.setDeviceInteractive(true); + when(mWakefullnessLifcycle.getWakefulness()).thenReturn( + WakefulnessLifecycle.WAKEFULNESS_AWAKE); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); - when(mStatusBar.isDeviceInteractive()).thenReturn(false); - when(mStatusBar.getWakefulnessState()).thenReturn( + mVolumeController.setDeviceInteractive(false); + when(mWakefullnessLifcycle.getWakefulness()).thenReturn( WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP); mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); } @Test - public void testVolumeChangeW_nullStatusBar() { - VolumeDialogControllerImpl.C callback = mock(VolumeDialogControllerImpl.C.class); - TestableVolumeDialogControllerImpl - nullStatusBarTestableDialog = - new TestableVolumeDialogControllerImpl( - mContext, callback, null, mBroadcastDispatcher, mRingerModeTracker); - nullStatusBarTestableDialog.setEnableDialogs(true, true); - nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI); - verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED); - } - - @Test public void testOnRemoteVolumeChanged_newStream_noNullPointer() { MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0); @@ -155,22 +166,51 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { verify(mRingerModeInternalLiveData).observeForever(any()); } - @Test - public void testRingerModeOnDestroy_observersRemoved() { - mVolumeController.destroy(); - - verify(mRingerModeLiveData).removeObserver(any()); - verify(mRingerModeInternalLiveData).removeObserver(any()); - } - static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { - TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s, - BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker) { - super( - context, broadcastDispatcher, - s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker); + private final WakefulnessLifecycle.Observer mWakefullessLifecycleObserver; + + TestableVolumeDialogControllerImpl( + Context context, + BroadcastDispatcher broadcastDispatcher, + RingerModeTracker ringerModeTracker, + ThreadFactory theadFactory, + AudioManager audioManager, + NotificationManager notificationManager, + Optional<Vibrator> optionalVibrator, + IAudioService iAudioService, + AccessibilityManager accessibilityManager, + PackageManager packageManager, + WakefulnessLifecycle wakefulnessLifecycle, + C callback) { + super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, + notificationManager, optionalVibrator, iAudioService, accessibilityManager, + packageManager, wakefulnessLifecycle); mCallbacks = callback; + + ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor = + ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class); + verify(wakefulnessLifecycle).addObserver(observerCaptor.capture()); + mWakefullessLifecycleObserver = observerCaptor.getValue(); + } + + public void setDeviceInteractive(boolean interactive) { + if (interactive) { + mWakefullessLifecycleObserver.onStartedWakingUp(); + } else { + mWakefullessLifecycleObserver.onFinishedGoingToSleep(); + } } } +// static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl { +// TestableVolumeDialogControllerImpl(Context context, C callback, +// BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker, +// ThreadFactory threadFactory) { +// super( +// context, broadcastDispatcher, +// s == null ? Optional.empty() : Optional.of(() -> s), ringerModeTracker); +// mCallbacks = callback; +// } +// } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java new file mode 100644 index 000000000000..33666bc5b462 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wallet.controller; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.service.quickaccesswallet.GetWalletCardsRequest; +import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.settings.SecureSettings; + +import com.google.common.util.concurrent.MoreExecutors; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class QuickAccessWalletControllerTest extends SysuiTestCase { + + @Mock + private QuickAccessWalletClient mQuickAccessWalletClient; + @Mock + private SecureSettings mSecureSettings; + @Mock + private QuickAccessWalletClient.OnWalletCardsRetrievedCallback mCardsRetriever; + @Captor + private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor; + + private QuickAccessWalletController mController; + private TestableLooper mTestableLooper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true); + + mController = new QuickAccessWalletController( + mContext, + MoreExecutors.directExecutor(), + mSecureSettings, + mQuickAccessWalletClient); + } + + @Test + public void walletEnabled() { + mController.updateWalletPreference(); + + assertTrue(mController.isWalletEnabled()); + } + + @Test + public void walletServiceUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void walletFeatureUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void walletFeatureWhenLockedUnavailable_walletNotEnabled() { + when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(false); + + mController.updateWalletPreference(); + + assertFalse(mController.isWalletEnabled()); + } + + @Test + public void getWalletClient_NoRecreation_sameClient() { + assertSame(mQuickAccessWalletClient, mController.getWalletClient()); + } + + @Test + public void getWalletClient_reCreateClient_notSameClient() { + mController.reCreateWalletClient(); + + assertNotSame(mQuickAccessWalletClient, mController.getWalletClient()); + } + + @Test + public void queryWalletCards_walletNotEnabled_notQuery() { + when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); + + mController.queryWalletCards(mCardsRetriever); + + verify(mQuickAccessWalletClient, never()).getWalletCards(any(), any(), any()); + } + + @Test + public void queryWalletCards_walletEnabled_queryCards() { + mController.updateWalletPreference(); + mController.queryWalletCards(mCardsRetriever); + + verify(mQuickAccessWalletClient) + .getWalletCards( + eq(MoreExecutors.directExecutor()), + mRequestCaptor.capture(), + eq(mCardsRetriever)); + + GetWalletCardsRequest request = mRequestCaptor.getValue(); + assertEquals(1, mRequestCaptor.getValue().getMaxCards()); + assertEquals( + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width), + request.getCardWidthPx()); + assertEquals( + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), + request.getCardHeightPx()); + } +} diff --git a/services/Android.bp b/services/Android.bp index f2e0b203c3fa..c83a697a71c1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -72,17 +72,6 @@ filegroup { visibility: ["//visibility:private"], } -filegroup { - name: "services-all-sources", - srcs: [ - ":services-non-updatable-sources", - ":service-media-s-sources", - ":service-permission-sources", - ":service-statsd-sources", - ], - visibility: ["//visibility:private"], -} - java_library { name: "Slogf", srcs: ["core/java/com/android/server/utils/Slogf.java"], diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 04083093d101..71d6a48b89ee 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2569,10 +2569,6 @@ public final class ActiveServices { s.setAllowedBgActivityStartsByBinding(true); } - if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - s.setAllowedBgFgsStartsByBinding(true); - } - if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) { s.isNotAppComponentUsage = true; } @@ -4129,9 +4125,6 @@ public final class ActiveServices { if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.updateIsAllowedBgActivityStartsByBinding(); } - if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - s.updateIsAllowedBgFgsStartsByBinding(); - } if (s.app != null) { updateServiceClientActivitiesLocked(s.app.mServices, c, true); } @@ -5856,8 +5849,6 @@ public final class ActiveServices { final ProcessStateRecord state = app.mState; if (state.isAllowedStartFgsState()) { return getReasonCodeFromProcState(state.getAllowStartFgsState()); - } else if (state.areBackgroundFgsStartsAllowedByToken()) { - return REASON_FGS_BINDING; } else { final ActiveInstrumentation instr = app.getActiveInstrumentation(); if (instr != null diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 88f4fc2d59f8..3e6a0a8ec80d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1236,7 +1236,7 @@ public class ActivityManagerService extends IActivityManager.Stub * The temp-allowlist that is allowed to start FGS from background. */ @CompositeRWLock({"this", "mProcLock"}) - final FgsTempAllowList<Integer, FgsTempAllowListItem> mFgsStartTempAllowList = + final FgsTempAllowList<FgsTempAllowListItem> mFgsStartTempAllowList = new FgsTempAllowList(); static final FgsTempAllowListItem FAKE_TEMP_ALLOW_LIST_ITEM = new FgsTempAllowListItem( @@ -1246,7 +1246,7 @@ public class ActivityManagerService extends IActivityManager.Stub * List of uids that are allowed to have while-in-use permission when FGS is started from * background. */ - private final FgsTempAllowList<Integer, String> mFgsWhileInUseTempAllowList = + private final FgsTempAllowList<String> mFgsWhileInUseTempAllowList = new FgsTempAllowList(); /** @@ -2589,6 +2589,7 @@ public class ActivityManagerService extends IActivityManager.Stub addServiceToMap(mAppBindArgs, Context.POWER_SERVICE); addServiceToMap(mAppBindArgs, Context.USER_SERVICE); addServiceToMap(mAppBindArgs, "mount"); + addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE); } return mAppBindArgs; } @@ -9371,22 +9372,17 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mFgsStartTempAllowList:"); final long currentTimeNow = System.currentTimeMillis(); final long elapsedRealtimeNow = SystemClock.elapsedRealtime(); - final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet()); - for (Integer uid : uids) { - final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid); - if (entry == null) { - continue; - } + mFgsStartTempAllowList.forEach((uid, entry) -> { pw.print(" " + UserHandle.formatUid(uid) + ": "); - entry.second.dump(pw); pw.println(); - pw.print("ms expiration="); + entry.second.dump(pw); + pw.print(" expiration="); // Convert entry.mExpirationTime, which is an elapsed time since boot, // to a time since epoch (i.e. System.currentTimeMillis()-based time.) final long expirationInCurrentTime = currentTimeNow - elapsedRealtimeNow + entry.first; TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow); pw.println(); - } + }); } if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -15344,10 +15340,19 @@ public class ActivityManagerService extends IActivityManager.Stub mDeviceIdleTempAllowlist = appids; if (adding) { if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + // Note, the device idle temp-allowlist are by app-ids, but here + // mFgsStartTempAllowList contains UIDs. mFgsStartTempAllowList.add(changingUid, durationMs, new FgsTempAllowListItem(durationMs, reasonCode, reason, callingUid)); } + } else { + // Note in the removing case, we need to remove all the UIDs matching + // the appId, because DeviceIdle's temp-allowlist are based on AppIds, + // not UIDs. + // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will + // not only remove this app for user 0, but for all users. + mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid)); } setAppIdTempAllowlistStateLSP(changingUid, adding); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6cb374a84ad0..4e6e91ac7b5d 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -771,9 +771,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } - // TODO(b/187223764): busTime won't be needed once end_session is a field in BUS. - final long busTime = System.currentTimeMillis(); - final byte[] statsProto = bus.getStatsProto(busTime); + final byte[] statsProto = bus.getStatsProto(); data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto)); diff --git a/services/core/java/com/android/server/am/FgsTempAllowList.java b/services/core/java/com/android/server/am/FgsTempAllowList.java index 847e82f07899..c28655655765 100644 --- a/services/core/java/com/android/server/am/FgsTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsTempAllowList.java @@ -20,11 +20,12 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import android.annotation.Nullable; import android.os.SystemClock; -import android.util.ArrayMap; +import android.os.UserHandle; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; -import java.util.Set; +import java.util.function.BiConsumer; /** * List of keys that have expiration time. @@ -33,19 +34,18 @@ import java.util.Set; * * <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p> * - * <p>Note: the underlying data structure is an {@link ArrayMap}, for performance reason, it is only - * suitable to hold up to hundreds of entries.</p> - * @param <K> type of the key. + * <p>Note: the underlying data structure is an {@link SparseArray}, for performance reason, + * it is only suitable to hold up to hundreds of entries.</p> * @param <E> type of the additional optional info. */ -public class FgsTempAllowList<K, E> { +public class FgsTempAllowList<E> { private static final int DEFAULT_MAX_SIZE = 100; /** * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime), * Pair.second is the optional information entry about this key. */ - private final ArrayMap<K, Pair<Long, E>> mTempAllowList = new ArrayMap<>(); + private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>(); private int mMaxSize = DEFAULT_MAX_SIZE; private final Object mLock = new Object(); @@ -70,15 +70,14 @@ public class FgsTempAllowList<K, E> { /** * Add a key and its duration with optional info into the temp allowlist. - * @param key * @param durationMs temp-allowlisted duration in milliseconds. * @param entry additional optional information of this key, could be null. */ - public void add(K key, long durationMs, @Nullable E entry) { + public void add(int uid, long durationMs, @Nullable E entry) { synchronized (mLock) { if (durationMs <= 0) { Slog.e(TAG_AM, "FgsTempAllowList bad duration:" + durationMs + " key: " - + key); + + uid); return; } // The temp allowlist should be a short list with only a few entries in it. @@ -94,10 +93,10 @@ public class FgsTempAllowList<K, E> { } } } - final Pair<Long, E> existing = mTempAllowList.get(key); + final Pair<Long, E> existing = mTempAllowList.get(uid); final long expirationTime = now + durationMs; if (existing == null || existing.first < expirationTime) { - mTempAllowList.put(key, new Pair(expirationTime, entry)); + mTempAllowList.put(uid, new Pair(expirationTime, entry)); } } } @@ -105,13 +104,12 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return its non-null value. * If the key has expired, return null. - * @param key * @return */ @Nullable - public Pair<Long, E> get(K key) { + public Pair<Long, E> get(int uid) { synchronized (mLock) { - final int index = mTempAllowList.indexOfKey(key); + final int index = mTempAllowList.indexOfKey(uid); if (index < 0) { return null; } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) { @@ -126,23 +124,48 @@ public class FgsTempAllowList<K, E> { /** * If the key has not expired (AKA allowed), return true. * If the key has expired, return false. - * @param key - * @return */ - public boolean isAllowed(K key) { - Pair<Long, E> entry = get(key); + public boolean isAllowed(int uid) { + Pair<Long, E> entry = get(uid); return entry != null; } - public void remove(K key) { + /** + * Remove a given UID. + */ + public void removeUid(int uid) { synchronized (mLock) { - mTempAllowList.remove(key); + mTempAllowList.remove(uid); } } - public Set<K> keySet() { + /** + * Remove by appId. + */ + public void removeAppId(int appId) { synchronized (mLock) { - return mTempAllowList.keySet(); + // Find all UIDs matching the appId. + for (int i = mTempAllowList.size() - 1; i >= 0; i--) { + final int uid = mTempAllowList.keyAt(i); + if (UserHandle.getAppId(uid) == appId) { + mTempAllowList.removeAt(i); + } + } + } + } + + /** + * Iterate over the entries. + */ + public void forEach(BiConsumer<Integer, Pair<Long, E>> callback) { + synchronized (mLock) { + for (int i = 0; i < mTempAllowList.size(); i++) { + final int uid = mTempAllowList.keyAt(i); + final Pair<Long, E> entry = mTempAllowList.valueAt(i); + if (entry != null) { + callback.accept(uid, entry); + } + } } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0ffaccfa0e89..457fe0f88aa7 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -3074,7 +3074,7 @@ public final class ProcessList { UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); - mService.mFgsStartTempAllowList.remove(record.info.uid); + mService.mFgsStartTempAllowList.removeUid(record.info.uid); mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT, ActivityManager.PROCESS_CAPABILITY_NONE); } diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index d83e13cb2e41..1fb5572cc5fe 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -26,7 +26,6 @@ import static com.android.server.am.ProcessRecord.TAG; import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; -import android.os.Binder; import android.os.SystemClock; import android.util.ArraySet; import android.util.Slog; @@ -302,9 +301,6 @@ final class ProcessStateRecord { @GuardedBy("mService") private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; - @GuardedBy("mService") - private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>(); - /** * Whether or not this process has been in forced-app-standby state. */ @@ -1101,21 +1097,6 @@ final class ProcessStateRecord { } @GuardedBy("mService") - void addAllowBackgroundFgsStartsToken(Binder entity) { - mBackgroundFgsStartTokens.add(entity); - } - - @GuardedBy("mService") - void removeAllowBackgroundFgsStartsToken(Binder entity) { - mBackgroundFgsStartTokens.remove(entity); - } - - @GuardedBy("mService") - boolean areBackgroundFgsStartsAllowedByToken() { - return !mBackgroundFgsStartTokens.isEmpty(); - } - - @GuardedBy("mService") void resetAllowStartFgsState() { mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index fd59e852ddd7..dbb2f65b0680 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -149,10 +149,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN @GuardedBy("ams") private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>(); - // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND - // flag? if true, the process can start FGS from background. - boolean mIsAllowedBgFgsStartsByBinding; - // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; @@ -445,10 +441,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart="); pw.println(mIsAllowedBgActivityStartsByStart); } - if (mIsAllowedBgFgsStartsByBinding) { - pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding="); - pw.println(mIsAllowedBgFgsStartsByBinding); - } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); @@ -634,11 +626,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } else { proc.removeAllowBackgroundActivityStartsToken(this); } - if (mIsAllowedBgFgsStartsByBinding) { - proc.mState.addAllowBackgroundFgsStartsToken(this); - } else { - proc.mState.removeAllowBackgroundFgsStartsToken(this); - } } if (app != null && app != proc) { // If the old app is allowed to start bg activities because of a service start, leave it @@ -726,34 +713,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN setAllowedBgActivityStartsByBinding(isAllowedByBinding); } - void updateIsAllowedBgFgsStartsByBinding() { - boolean isAllowedByBinding = false; - for (int conni = connections.size() - 1; conni >= 0; conni--) { - ArrayList<ConnectionRecord> cr = connections.valueAt(conni); - for (int i = 0; i < cr.size(); i++) { - if ((cr.get(i).flags - & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { - isAllowedByBinding = true; - break; - } - } - if (isAllowedByBinding) { - break; - } - } - setAllowedBgFgsStartsByBinding(isAllowedByBinding); - } - void setAllowedBgActivityStartsByBinding(boolean newValue) { mIsAllowedBgActivityStartsByBinding = newValue; updateParentProcessBgActivityStartsToken(); } - void setAllowedBgFgsStartsByBinding(boolean newValue) { - mIsAllowedBgFgsStartsByBinding = newValue; - updateParentProcessBgFgsStartsToken(); - } - /** * Called when the service is started with allowBackgroundActivityStarts set. We allow * it for background activity starts, setting up a callback to remove this ability after a @@ -846,17 +810,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } - private void updateParentProcessBgFgsStartsToken() { - if (app == null) { - return; - } - if (mIsAllowedBgFgsStartsByBinding) { - app.mState.addAllowBackgroundFgsStartsToken(this); - } else { - app.mState.removeAllowBackgroundFgsStartsToken(this); - } - } - /** * Returns the originating token if that's the only reason background activity starts are * allowed. In order for that to happen the service has to be allowed only due to starts, since diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 3f2b8fffcc54..b714c6d73613 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -33,6 +33,9 @@ import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Handler; import android.os.SystemClock; import android.os.SystemProperties; @@ -126,6 +129,7 @@ class RebootEscrowManager { ERROR_UNLOCK_ALL_USERS, ERROR_PROVIDER_MISMATCH, ERROR_KEYSTORE_FAILURE, + ERROR_NO_NETWORK, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -139,6 +143,7 @@ class RebootEscrowManager { static final int ERROR_UNLOCK_ALL_USERS = 5; static final int ERROR_PROVIDER_MISMATCH = 6; static final int ERROR_KEYSTORE_FAILURE = 7; + static final int ERROR_NO_NETWORK = 8; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -235,6 +240,23 @@ class RebootEscrowManager { "server_based_ror_enabled", false); } + public boolean isNetworkConnected() { + final ConnectivityManager connectivityManager = + mContext.getSystemService(ConnectivityManager.class); + if (connectivityManager == null) { + return false; + } + + Network activeNetwork = connectivityManager.getActiveNetwork(); + NetworkCapabilities networkCapabilities = + connectivityManager.getNetworkCapabilities(activeNetwork); + return networkCapabilities != null + && networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_INTERNET) + && networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + public Context getContext() { return mContext; } @@ -363,7 +385,11 @@ class RebootEscrowManager { } Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); - mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; + if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) { + mLoadEscrowDataErrorCode = ERROR_NO_NETWORK; + } else { + mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; + } onGetRebootEscrowKeyFailed(users, attemptNumber); } @@ -471,6 +497,8 @@ class RebootEscrowManager { mLoadEscrowDataErrorCode = ERROR_UNKNOWN; } + Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: " + + serviceType + ", error code: " + mLoadEscrowDataErrorCode); // TODO(179105110) report the duration since boot complete. mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, -1); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0bc28405eab3..f0e693976faa 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8469,7 +8469,9 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < newUris.size(); i++) { final Uri uri = newUris.valueAt(i); if (oldUris == null || !oldUris.contains(uri)) { - Slog.d(TAG, key + ": granting " + uri); + if (DBG) { + Slog.d(TAG, key + ": granting " + uri); + } grantUriPermission(permissionOwner, uri, newRecord.getUid(), targetPkg, targetUserId); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index de9add0d0b37..b135e88f40c7 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1207,8 +1207,16 @@ public class LauncherAppsService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { String packageName = component.getPackageName(); + int uId = -1; + try { + uId = mContext.getPackageManager().getApplicationInfo( + packageName, PackageManager.MATCH_ANY_USER).uid; + } catch (PackageManager.NameNotFoundException e) { + Log.d(TAG, "package not found: " + e); + } intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null)); + intent.putExtra("uId", uId); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setSourceBounds(sourceBounds); } finally { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 983b5b17bb92..9c25159faef1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -192,6 +192,7 @@ import android.view.autofill.AutofillManagerInternal; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.app.AssistUtils; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -650,7 +651,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LAUNCH_ASSIST: final int deviceId = msg.arg1; final Long eventTime = (Long) msg.obj; - launchAssistAction(null /* hint */, deviceId, eventTime); + launchAssistAction(null /* hint */, deviceId, eventTime, + AssistUtils.INVOCATION_TYPE_UNKNOWN); break; case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: launchVoiceAssistWithWakeLock(); @@ -1108,7 +1110,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false, "Power - Long Press - Go To Assistant"); final int powerKeyDeviceId = Integer.MIN_VALUE; - launchAssistAction(null, powerKeyDeviceId, eventTime); + launchAssistAction(null, powerKeyDeviceId, eventTime, + AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS); break; } } @@ -1541,7 +1544,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { launchAllAppsAction(); break; case LONG_PRESS_HOME_ASSIST: - launchAssistAction(null, deviceId, eventTime); + launchAssistAction(null, deviceId, eventTime, + AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS); break; case LONG_PRESS_HOME_NOTIFICATION_PANEL: toggleNotificationPanel(); @@ -2772,7 +2776,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (mPendingMetaAction) { launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId(), - event.getEventTime()); + event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN); mPendingMetaAction = false; } } @@ -3036,7 +3040,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // various parts of the UI. /** Asks the status bar to startAssist(), usually a full "assistant" interface */ - private void launchAssistAction(String hint, int deviceId, long eventTime) { + private void launchAssistAction(String hint, int deviceId, long eventTime, + int invocationType) { sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); if (!isUserSetupComplete()) { // Disable opening assist window during setup @@ -3053,6 +3058,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { args.putBoolean(hint, true); } args.putLong(Intent.EXTRA_TIME, eventTime); + args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType); ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0) .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args); diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java index d6060fa419b9..88c9850966f1 100644 --- a/services/core/java/com/android/server/power/WakeLockLog.java +++ b/services/core/java/com/android/server/power/WakeLockLog.java @@ -16,16 +16,12 @@ package com.android.server.power; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.PowerManager; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.internal.os.SomeArgs; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -74,8 +70,6 @@ final class WakeLockLog { private static final boolean DEBUG = false; - private static final int MSG_ON_WAKE_LOCK_EVENT = 1; - private static final int TYPE_TIME_RESET = 0x0; private static final int TYPE_ACQUIRE = 0x1; private static final int TYPE_RELEASE = 0x2; @@ -130,7 +124,6 @@ final class WakeLockLog { private final Injector mInjector; private final TheLog mLog; private final TagDatabase mTagDatabase; - private final Handler mHandler; private final SimpleDateFormat mDumpsysDateFormat; WakeLockLog() { @@ -140,7 +133,6 @@ final class WakeLockLog { @VisibleForTesting WakeLockLog(Injector injector) { mInjector = injector; - mHandler = new WakeLockLogHandler(injector.getLooper()); mTagDatabase = new TagDatabase(injector); EntryByteTranslator translator = new EntryByteTranslator(mTagDatabase); mLog = new TheLog(injector, translator, mTagDatabase); @@ -172,7 +164,7 @@ final class WakeLockLog { * Dumps all the wake lock data currently saved in the wake lock log to the specified * {@code PrintWriter}. * - * @param The {@code PrintWriter} to write to. + * @param pw The {@code PrintWriter} to write to. */ public void dump(PrintWriter pw) { dump(pw, false); @@ -221,9 +213,6 @@ final class WakeLockLog { /** * Adds a new entry to the log based on the specified wake lock parameters. * - * Grabs the current time for the event and then posts the rest of the logic (actually - * adding it to the log) to a background thread. - * * @param eventType The type of event (ACQUIRE, RELEASE); * @param tag The wake lock's identifying tag. * @param ownerUid The owner UID of the wake lock. @@ -239,15 +228,11 @@ final class WakeLockLog { } final long time = mInjector.currentTimeMillis(); - - SomeArgs args = SomeArgs.obtain(); - args.arg1 = tagNameReducer(tag); - args.argi1 = eventType; - args.argi2 = ownerUid; - args.argi3 = eventType == TYPE_ACQUIRE ? translateFlagsFromPowerManager(flags) : 0; - args.argi4 = (int) ((time >> 32) & 0xFFFFFFFFL); - args.argi5 = (int) (time & 0xFFFFFFFFL); - mHandler.obtainMessage(MSG_ON_WAKE_LOCK_EVENT, args).sendToTarget(); + final int translatedFlags = eventType == TYPE_ACQUIRE + ? translateFlagsFromPowerManager(flags) + : 0; + handleWakeLockEventInternal(eventType, tagNameReducer(tag), ownerUid, translatedFlags, + time); } /** @@ -273,8 +258,8 @@ final class WakeLockLog { * flags, {@code WakeLockLog.FLAG_*}, and a log-level, {@code WakeLockLog.LEVEL_*}. * * @param flags Wake lock flags including {@code PowerManager.*_WAKE_LOCK} - * {@link PowerManager.ACQUIRE_CAUSES_WAKEUP}, and - * {@link PowerManager.ON_AFTER_RELEASE}. + * {@link PowerManager#ACQUIRE_CAUSES_WAKEUP}, and + * {@link PowerManager#ON_AFTER_RELEASE}. * @return The compressed flags value. */ int translateFlagsFromPowerManager(int flags) { @@ -328,9 +313,9 @@ final class WakeLockLog { } String reduciblePrefix = null; - for (int tp = 0; tp < REDUCED_TAG_PREFIXES.length; tp++) { - if (tag.startsWith(REDUCED_TAG_PREFIXES[tp])) { - reduciblePrefix = REDUCED_TAG_PREFIXES[tp]; + for (String reducedTagPrefix : REDUCED_TAG_PREFIXES) { + if (tag.startsWith(reducedTagPrefix)) { + reduciblePrefix = reducedTagPrefix; break; } } @@ -339,7 +324,7 @@ final class WakeLockLog { final StringBuilder sb = new StringBuilder(); // add prefix first - sb.append(tag.substring(0, reduciblePrefix.length())); + sb.append(tag, 0, reduciblePrefix.length()); // Stop looping on final marker final int end = Math.max(tag.lastIndexOf("/"), tag.lastIndexOf(".")); @@ -604,7 +589,7 @@ final class WakeLockLog { * @return The number of bytes written to buffer, or required to write to the buffer. */ int toBytes(LogEntry entry, byte[] bytes, long timeReference) { - int sizeNeeded = -1; + final int sizeNeeded; switch (entry.type) { case TYPE_ACQUIRE: { sizeNeeded = 3; @@ -696,8 +681,9 @@ final class WakeLockLog { * {@link EntryByteTranslator} to convert byte {@link LogEntry} to bytes within the buffer. * * This class also implements the logic around TIME_RESET events. Since the LogEntries store - * their time (8-bit) relative to the previous event, this class can add {@link TYPE_TIME_RESET} - * LogEntries as necessary to allow a LogEntry's relative time to fit within that range. + * their time (8-bit) relative to the previous event, this class can add + * {@link #TYPE_TIME_RESET} LogEntries as necessary to allow a LogEntry's relative time to fit + * within that range. */ static class TheLog { private final EntryByteTranslator mTranslator; @@ -711,7 +697,7 @@ final class WakeLockLog { /** * Second temporary buffer used when reading and writing bytes from the buffer. * A second temporary buffer is necessary since additional items can be read concurrently - * from {@link mTempBuffer}. E.g., Adding an entry to a full buffer requires removing + * from {@link #mTempBuffer}. E.g., Adding an entry to a full buffer requires removing * other entries from the buffer. */ private final byte[] mReadWriteTempBuffer = new byte[MAX_LOG_ENTRY_BYTE_SIZE]; @@ -832,7 +818,7 @@ final class WakeLockLog { * Returns an {@link Iterator} of {@link LogEntry}s for all the entries in the log. * * If the log is modified while the entries are being read, the iterator will throw a - * {@link ConcurrentModificationExceptoin}. + * {@link ConcurrentModificationException}. * * @param tempEntry A temporary {@link LogEntry} instance to use so that new instances * aren't allocated with every call to {@code Iterator.next}. @@ -1063,7 +1049,7 @@ final class WakeLockLog { * instanced into bytes. * * If a new tag is added when the database is full, the oldest tag is removed. The oldest tag - * is calcualted using {@link TagData.lastUsedTime}. + * is calculated using {@link TagData#lastUsedTime}. */ static class TagDatabase { private final int mInvalidIndex; @@ -1086,9 +1072,9 @@ final class WakeLockLog { int byteEstimate = 0; int tagSize = 0; int tags = 0; - for (int i = 0; i < mArray.length; i++) { + for (TagData tagData : mArray) { byteEstimate += 8; // reference pointer - TagData data = mArray[i]; + TagData data = tagData; if (data != null) { entries++; byteEstimate += data.getByteSize(); @@ -1280,10 +1266,6 @@ final class WakeLockLog { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - if (DEBUG) { - sb.append("(").append(index).append(")"); - } return "[" + ownerUid + " ; " + tag + "]"; } @@ -1308,10 +1290,6 @@ final class WakeLockLog { * Injector used by {@link WakeLockLog} for testing purposes. */ public static class Injector { - public Looper getLooper() { - return BackgroundThread.get().getLooper(); - } - public int getTagDatabaseSize() { return TAG_DATABASE_SIZE; } @@ -1328,28 +1306,4 @@ final class WakeLockLog { return DATE_FORMAT; } } - - private class WakeLockLogHandler extends Handler { - WakeLockLogHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message message) { - switch(message.what) { - case MSG_ON_WAKE_LOCK_EVENT: - final SomeArgs args = (SomeArgs) message.obj; - final String tag = (String) args.arg1; - final int eventType = args.argi1; - final int ownerUid = args.argi2; - final int flags = args.argi3; - final long time = (((long) args.argi4) << 32) + (args.argi5 & 0xFFFFFFFFL); - args.recycle(); - handleWakeLockEventInternal(eventType, tag, ownerUid, flags, time); - break; - default: - break; - } - } - } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index a82c91e2352b..dc868b325900 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -126,6 +126,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.connectivity.WifiActivityEnergyInfo; +import android.os.incremental.IncrementalManager; import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; @@ -426,6 +427,7 @@ public class StatsPullAtomService extends SystemService { private final Object mHealthHalLock = new Object(); private final Object mAttributedAppOpsLock = new Object(); private final Object mSettingsStatsLock = new Object(); + private final Object mInstalledIncrementalPackagesLock = new Object(); public StatsPullAtomService(Context context) { super(context); @@ -695,6 +697,10 @@ public class StatsPullAtomService extends SystemService { synchronized (mSettingsStatsLock) { return pullSettingsStatsLocked(atomTag, data); } + case FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE: + synchronized (mInstalledIncrementalPackagesLock) { + return pullInstalledIncrementalPackagesLocked(atomTag, data); + } default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -877,6 +883,7 @@ public class StatsPullAtomService extends SystemService { registerBatteryVoltage(); registerBatteryCycleCount(); registerSettingsStats(); + registerInstalledIncrementalPackages(); } private void initAndRegisterNetworkStatsPullers() { @@ -3949,6 +3956,31 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerInstalledIncrementalPackages() { + int tagId = FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE; + mStatsManager.setPullAtomCallback( + tagId, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } + + int pullInstalledIncrementalPackagesLocked(int atomTag, List<StatsEvent> pulledData) { + final PackageManager pm = mContext.getPackageManager(); + if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) { + // Incremental is not enabled on this device. The result list will be empty. + return StatsManager.PULL_SUCCESS; + } + List<PackageInfo> installedPackages = pm.getInstalledPackages(0); + for (PackageInfo pi : installedPackages) { + if (IncrementalManager.isIncrementalPath(pi.applicationInfo.getBaseCodePath())) { + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid)); + } + } + return StatsManager.PULL_SUCCESS; + } + // Thermal event received from vendor thermal management subsystem private static final class ThermalEventListener extends IThermalEventListener.Stub { @Override diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 737e7d10c3b4..2feb8a743930 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -1163,9 +1163,6 @@ public class RecentsAnimationController implements DeathRecipient { // Apply the task's pending transaction in case it is detached and its transaction // is not reachable. mTask.getPendingTransaction().apply(); - - // Reset whether this task can affect the sysui flags - mTask.setCanAffectSystemUiFlags(true); } } diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 50749a961b27..9d8b8f76ad40 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -372,7 +372,7 @@ class ScreenRotationAnimation { mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_exit); mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, - R.anim.screen_rotate_0_enter); + R.anim.rotation_animation_enter); break; case Surface.ROTATION_90: mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 93fc4f2a3149..039422d2c6a4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5407,17 +5407,17 @@ class Task extends WindowContainer<WindowContainer> { : WINDOWING_MODE_FULLSCREEN; } if (currentMode == WINDOWING_MODE_PINNED) { + mRootWindowContainer.notifyActivityPipModeChanged(null); + } + if (likelyResolvedMode == WINDOWING_MODE_PINNED) { // In the case that we've disabled affecting the SysUI flags as a part of seamlessly // transferring the transform on the leash to the task, reset this state once we've // actually entered pip setCanAffectSystemUiFlags(true); - mRootWindowContainer.notifyActivityPipModeChanged(null); - } - if (likelyResolvedMode == WINDOWING_MODE_PINNED - && taskDisplayArea.getRootPinnedTask() != null) { - - // Can only have 1 pip at a time, so replace an existing pip - taskDisplayArea.getRootPinnedTask().dismissPip(); + if (taskDisplayArea.getRootPinnedTask() != null) { + // Can only have 1 pip at a time, so replace an existing pip + taskDisplayArea.getRootPinnedTask().dismissPip(); + } } if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN && topActivity != null && !topActivity.noDisplay diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 68cb8f94aa0a..3dc7bc1154ea 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -48,14 +48,18 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; import android.os.Build; import android.os.Looper; import android.os.SystemClock; @@ -70,6 +74,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -83,6 +88,8 @@ public class ConnectivityControllerTest { @Mock private Context mContext; @Mock + private BatteryManagerInternal mBatteryManagerInternal; + @Mock private ConnectivityManager mConnManager; @Mock private NetworkPolicyManager mNetPolicyManager; @@ -108,6 +115,9 @@ public class ConnectivityControllerTest { LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); + LocalServices.removeServiceForTest(BatteryManagerInternal.class); + LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal); + when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); // Freeze the clocks at this moment in time @@ -143,8 +153,18 @@ public class ConnectivityControllerTest { DataUnit.MEBIBYTES.toBytes(1)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + final ArgumentCaptor<BroadcastReceiver> chargingCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(false); final ConnectivityController controller = new ConnectivityController(mService); + verify(mContext).registerReceiver(chargingCaptor.capture(), + ArgumentMatchers.argThat(filter -> + filter.hasAction(BatteryManager.ACTION_CHARGING) + && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L); + final BroadcastReceiver chargingReceiver = chargingCaptor.getValue(); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, @@ -166,7 +186,18 @@ public class ConnectivityControllerTest { assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); + // Slow network is too slow, but device is charging and network is unmetered. + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(true); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + assertTrue(controller.isSatisfied(createJobStatus(job), net, + createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) + .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), + mConstants)); + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(false); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L); // Slow network is too slow @@ -189,6 +220,14 @@ public class ConnectivityControllerTest { assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); + // Slow network is too slow, but device is charging and network is unmetered. + when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) + .thenReturn(true); + chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + assertTrue(controller.isSatisfied(createJobStatus(job), net, + createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) + .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), + mConstants)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java index f85f0f843789..50d4d8475395 100644 --- a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java +++ b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java @@ -29,6 +29,9 @@ import android.util.Pair; import org.junit.Test; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + /** * Build/Install/Run: * atest FrameworksServicesTests:TempAllowListTest @@ -41,7 +44,7 @@ public class FgsTempAllowListTest { */ @Test public void testIsAllowed() { - FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); + FgsTempAllowList<String> allowList = new FgsTempAllowList(); allowList.add(10001, 2000, "description1"); allowList.add(10002, 2000, "description2"); @@ -55,7 +58,7 @@ public class FgsTempAllowListTest { assertNotNull(entry2); assertEquals(entry2.second, "description2"); - allowList.remove(10001); + allowList.removeUid(10001); assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } @@ -65,7 +68,7 @@ public class FgsTempAllowListTest { */ @Test public void testExpired() { - FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList(); + FgsTempAllowList<String> allowList = new FgsTempAllowList(); // temp allow for 2000ms. allowList.add(10001, 2000, "uid1-2000ms"); // sleep for 3000ms. @@ -74,4 +77,51 @@ public class FgsTempAllowListTest { assertFalse(allowList.isAllowed(10001)); assertNull(allowList.get(10001)); } + + @Test + public void testRemoveAppId() { + FgsTempAllowList<String> allowList = new FgsTempAllowList(); + allowList.add(10001, 2000, "description1"); + allowList.add(10002, 2000, "description2"); + allowList.add(10_10001, 2000, "description3"); + + assertTrue(allowList.isAllowed(10001)); + assertTrue(allowList.isAllowed(10002)); + assertTrue(allowList.isAllowed(10_10001)); + + allowList.removeAppId(10001); + + assertFalse(allowList.isAllowed(10001)); + assertTrue(allowList.isAllowed(10002)); + assertFalse(allowList.isAllowed(10_10001)); + } + + @Test + public void testForEach() { + final FgsTempAllowList<String> allowList = new FgsTempAllowList(); + + + // Call forEach(), return the sum of all the UIDs, and make sure the item is + // "uid" + uid. + final Supplier<Integer> callForEach = () -> { + final AtomicInteger sum = new AtomicInteger(); + sum.set(0); + allowList.forEach((uid, entry) -> { + sum.set(sum.get() + uid); + assertEquals(entry.second, "uid" + uid); + }); + return sum.get(); + }; + + // Call on th empty list. + assertEquals(0, (int) callForEach.get()); + + // Add one item. + allowList.add(1, 2000, "uid1"); + assertEquals(1, (int) callForEach.get()); + + // Add one more item. + allowList.add(10, 2000, "uid10"); + assertEquals(11, (int) callForEach.get()); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java index 4cd17e877689..b820df3871ce 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java @@ -37,7 +37,6 @@ import android.os.IThermalService; import android.os.Looper; import android.os.PowerManager; import android.os.test.TestLooper; -import android.provider.Settings; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -51,6 +50,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collections; /** Tests for {@link OneTouchPlayAction} */ @SmallTest @@ -69,6 +69,7 @@ public class OneTouchPlayActionTest { private Context mContextSpy; private HdmiControlService mHdmiControlService; private FakeNativeWrapper mNativeWrapper; + private FakeHdmiCecConfig mHdmiCecConfig; private TestLooper mTestLooper = new TestLooper(); private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); @@ -88,6 +89,7 @@ public class OneTouchPlayActionTest { MockitoAnnotations.initMocks(this); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy); setHdmiControlEnabled(hdmiControlEnabled); @@ -99,7 +101,7 @@ public class OneTouchPlayActionTest { mIThermalServiceMock, new Handler(mTestLooper.getLooper()))); when(mIPowerManagerMock.isInteractive()).thenReturn(true); - mHdmiControlService = new HdmiControlService(mContextSpy) { + mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) { @Override AudioManager getAudioManager() { return new AudioManager() { @@ -134,7 +136,7 @@ public class OneTouchPlayActionTest { Looper looper = mTestLooper.getLooper(); mHdmiControlService.setIoLooper(looper); - mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(mContextSpy)); + mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig); mNativeWrapper = new FakeNativeWrapper(); HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper( this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); @@ -479,7 +481,7 @@ public class OneTouchPlayActionTest { mTestLooper.dispatchAll(); assertThat(callback.hasResult()).isFalse(); - assertThat(playbackDevice.isActiveSource()).isFalse(); + mNativeWrapper.clearResultMessages(); setHdmiControlEnabled(true); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); @@ -498,6 +500,12 @@ public class OneTouchPlayActionTest { assertThat(mHdmiControlService.isAddressAllocated()).isTrue(); assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); assertThat(playbackDevice.isActiveSource()).isTrue(); + HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( + playbackDevice.mAddress, mPhysicalAddress); + HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress, + ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(activeSource); + assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn); } @Test @@ -527,6 +535,12 @@ public class OneTouchPlayActionTest { assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); assertThat(playbackDevice.isActiveSource()).isTrue(); + HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( + playbackDevice.mAddress, mPhysicalAddress); + HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress, + ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(activeSource); + assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn); } @Test @@ -602,8 +616,8 @@ public class OneTouchPlayActionTest { } private void setHdmiControlEnabled(boolean enabled) { - int value = enabled ? 1 : 0; - Settings.Global.putInt(mContextSpy.getContentResolver(), - Settings.Global.HDMI_CONTROL_ENABLED, value); + int value = enabled ? HdmiControlManager.HDMI_CEC_CONTROL_ENABLED : + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED; + mHdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, value); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index aecc7942b266..b01c1c8ead28 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -165,7 +165,17 @@ public class RebootEscrowManagerTests { mRebootEscrow = null; mServerBased = true; RebootEscrowProviderServerBasedImpl.Injector injector = - new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection); + new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) { + @Override + long getServiceTimeoutInSeconds() { + return 30; + } + + @Override + long getServerBlobLifetimeInMillis() { + return 600_000; + } + }; mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl( storage, injector); mUserManager = userManager; @@ -189,6 +199,11 @@ public class RebootEscrowManagerTests { } @Override + public boolean isNetworkConnected() { + return false; + } + + @Override public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() { mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider; return mRebootEscrowProviderInUse; @@ -602,7 +617,7 @@ public class RebootEscrowManagerTests { // Sleep 5s for the retry to complete Thread.sleep(5 * 1000); assertFalse(metricsSuccessCaptor.getValue()); - assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK), metricsErrorCodeCaptor.getValue()); } diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java index a03ba9c5e87e..09612e33b98a 100644 --- a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java +++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java @@ -20,12 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.os.Looper; import android.os.PowerManager; -import android.os.test.TestLooper; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import java.io.PrintWriter; @@ -38,17 +34,6 @@ import java.util.TimeZone; */ public class WakeLockLogTest { - private TestLooper mTestLooper; - - @Before - public void setUp() throws Exception { - mTestLooper = new TestLooper(); - } - - @After - public void tearDown() throws Exception { - } - @Test public void testAddTwoItems() { final int tagDatabaseSize = 128; @@ -70,7 +55,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 2, Time-Resets: 0\n" + " Buffer, Bytes used: 6\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -92,7 +77,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 2, Time-Resets: 1\n" + " Buffer, Bytes used: 15\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -114,7 +99,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 2, Time-Resets: 0\n" + " Buffer, Bytes used: 6\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -142,7 +127,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 3, Time-Resets: 0\n" + " Buffer, Bytes used: 9\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -160,7 +145,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 0, Time-Resets: 0\n" + " Buffer, Bytes used: 0\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -179,7 +164,7 @@ public class WakeLockLogTest { + " -\n" + " Events: 1, Time-Resets: 0\n" + " Buffer, Bytes used: 3\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } @Test @@ -201,7 +186,7 @@ public class WakeLockLogTest { + " Events: 2, Time-Resets: 0\n" + " Buffer, Bytes used: 5\n" + " Tag Database: size(5), entries: 1, Bytes used: 80\n", - dispatchAndDump(log, true)); + dumpLog(log, true)); } @Test @@ -223,11 +208,10 @@ public class WakeLockLogTest { + " -\n" + " Events: 1, Time-Resets: 0\n" + " Buffer, Bytes used: 3\n", - dispatchAndDump(log, false)); + dumpLog(log, false)); } - private String dispatchAndDump(WakeLockLog log, boolean includeTagDb) { - mTestLooper.dispatchAll(); + private String dumpLog(WakeLockLog log, boolean includeTagDb) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); log.dump(pw, includeTagDb); @@ -244,11 +228,6 @@ public class WakeLockLogTest { } @Override - public Looper getLooper() { - return mTestLooper.getLooper(); - } - - @Override public int getTagDatabaseSize() { return mTagDatabaseSize; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c527e66bae29..6b7fc2fb4b7d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3682,6 +3682,13 @@ public class CarrierConfigManager { "emergency_number_prefix_string_array"; /** + * Indicates whether carrier treats "*67" or "*82" as a temporary mode CLIR. + * @hide + */ + public static final String KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL = + "carrier_supports_caller_id_vertical_service_codes_bool"; + + /** * Smart forwarding config. Smart forwarding is a feature to configure call forwarding to a * different SIM in the device when one SIM is not reachable. The config here specifies a smart * forwarding component that will launch UI for changing the configuration. An empty string @@ -5496,6 +5503,7 @@ public class CarrierConfigManager { 1 /* Roaming Indicator Off */ }); sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]); + sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, false); sDefaults.putBoolean(KEY_USE_USIM_BOOL, false); sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false); sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true); diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index 2b4fb7d5cbf3..734b52018857 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -79,7 +79,7 @@ public interface DelegateStateCallback { * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration * change event to reduce conditions where the remote application is using a stale IMS * configuration. - * @deprecated This is being removed from API surface, Use + * @removed This is being removed from API surface, Use * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead. */ @Deprecated diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index 08513c23291a..fe14dd18d1ab 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -34,7 +34,7 @@ import java.net.InetSocketAddress; /** * @hide - * @deprecated Use {@link SipDelegateConfiguration} instead. + * @removed Use {@link SipDelegateConfiguration} instead. */ @Deprecated @SystemApi diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java index c078637e3791..42c53f2a0caf 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -136,7 +136,7 @@ public interface DelegateConnectionStateCallback { * not compleed yet. * * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. - * @deprecated Will not be in final API, use + * @removed Will not be in final API, use * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}. */ @Deprecated @@ -161,7 +161,7 @@ public interface DelegateConnectionStateCallback { * * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network. */ - default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {} + void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig); /** * The previously created {@link SipDelegateConnection} instance delivered via diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 91ecbf081919..a096c1f3b8b2 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2440,6 +2440,12 @@ interface ITelephony { boolean removeUceRequestDisallowedStatus(int subId); /** + * Set the timeout for contact capabilities request. + * Note: This is designed for a SHELL command only. + */ + boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfterMs); + + /** * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the * specified thresholds. */ diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index 99aaa3c4d6e5..81d35efaf29f 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -4,6 +4,9 @@ LOCAL_DIR="$( dirname "${BASH_SOURCE}" )" if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 +elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then + # Change is explicitly marked as ok to skip AOSP + exit 0 else # Change appears to be non-AOSP; search for files count=0 diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py new file mode 100644 index 000000000000..297d9c3bcca0 --- /dev/null +++ b/tools/apilint/deprecated_at_birth.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python + +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Usage: deprecated_at_birth.py path/to/next/ path/to/previous/ +Usage: deprecated_at_birth.py prebuilts/sdk/31/public/api/ prebuilts/sdk/30/public/api/ +""" + +import re, sys, os, collections, traceback, argparse + + +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + +def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): + # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes + codes = [] + if reset: codes.append("0") + else: + if not fg is None: codes.append("3%d" % (fg)) + if not bg is None: + if not bright: codes.append("4%d" % (bg)) + else: codes.append("10%d" % (bg)) + if bold: codes.append("1") + elif dim: codes.append("2") + else: codes.append("22") + return "\033[%sm" % (";".join(codes)) + + +def ident(raw): + """Strips superficial signature changes, giving us a strong key that + can be used to identify members across API levels.""" + raw = raw.replace(" deprecated ", " ") + raw = raw.replace(" synchronized ", " ") + raw = raw.replace(" final ", " ") + raw = re.sub("<.+?>", "", raw) + raw = re.sub("@[A-Za-z]+ ", "", raw) + raw = re.sub("@[A-Za-z]+\(.+?\) ", "", raw) + if " throws " in raw: + raw = raw[:raw.index(" throws ")] + return raw + + +class Field(): + def __init__(self, clazz, line, raw, blame): + self.clazz = clazz + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + raw = raw.split() + self.split = list(raw) + + raw = [ r for r in raw if not r.startswith("@") ] + for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1].strip(";") + if len(raw) >= 4 and raw[2] == "=": + self.value = raw[3].strip(';"') + else: + self.value = None + self.ident = ident(self.raw) + + def __hash__(self): + return hash(self.raw) + + def __repr__(self): + return self.raw + + +class Method(): + def __init__(self, clazz, line, raw, blame): + self.clazz = clazz + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + # drop generics for now + raw = re.sub("<.+?>", "", raw) + + raw = re.split("[\s(),;]+", raw) + for r in ["", ";"]: + while r in raw: raw.remove(r) + self.split = list(raw) + + raw = [ r for r in raw if not r.startswith("@") ] + for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1] + self.args = [] + self.throws = [] + target = self.args + for r in raw[2:]: + if r == "throws": target = self.throws + else: target.append(r) + self.ident = ident(self.raw) + + def __hash__(self): + return hash(self.raw) + + def __repr__(self): + return self.raw + + +class Class(): + def __init__(self, pkg, line, raw, blame): + self.pkg = pkg + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + self.ctors = [] + self.fields = [] + self.methods = [] + + raw = raw.split() + self.split = list(raw) + if "class" in raw: + self.fullname = raw[raw.index("class")+1] + elif "enum" in raw: + self.fullname = raw[raw.index("enum")+1] + elif "interface" in raw: + self.fullname = raw[raw.index("interface")+1] + elif "@interface" in raw: + self.fullname = raw[raw.index("@interface")+1] + else: + raise ValueError("Funky class type %s" % (self.raw)) + + if "extends" in raw: + self.extends = raw[raw.index("extends")+1] + self.extends_path = self.extends.split(".") + else: + self.extends = None + self.extends_path = [] + + self.fullname = self.pkg.name + "." + self.fullname + self.fullname_path = self.fullname.split(".") + + self.name = self.fullname[self.fullname.rindex(".")+1:] + + def __hash__(self): + return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods))) + + def __repr__(self): + return self.raw + + +class Package(): + def __init__(self, line, raw, blame): + self.line = line + self.raw = raw.strip(" {;") + self.blame = blame + + raw = raw.split() + self.name = raw[raw.index("package")+1] + self.name_path = self.name.split(".") + + def __repr__(self): + return self.raw + + +def _parse_stream(f, api={}): + line = 0 + pkg = None + clazz = None + blame = None + + re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") + for raw in f: + line += 1 + raw = raw.rstrip() + match = re_blame.match(raw) + if match is not None: + blame = match.groups()[0:2] + raw = match.groups()[2] + else: + blame = None + + if raw.startswith("package"): + pkg = Package(line, raw, blame) + elif raw.startswith(" ") and raw.endswith("{"): + clazz = Class(pkg, line, raw, blame) + api[clazz.fullname] = clazz + elif raw.startswith(" ctor"): + clazz.ctors.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" method"): + clazz.methods.append(Method(clazz, line, raw, blame)) + elif raw.startswith(" field"): + clazz.fields.append(Field(clazz, line, raw, blame)) + + return api + + +def _parse_stream_path(path): + api = {} + print "Parsing", path + for f in os.listdir(path): + f = os.path.join(path, f) + if not os.path.isfile(f): continue + if not f.endswith(".txt"): continue + if f.endswith("removed.txt"): continue + print "\t", f + with open(f) as s: + api = _parse_stream(s, api) + print "Parsed", len(api), "APIs" + print + return api + + +class Failure(): + def __init__(self, sig, clazz, detail, error, rule, msg): + self.sig = sig + self.error = error + self.rule = rule + self.msg = msg + + if error: + self.head = "Error %s" % (rule) if rule else "Error" + dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg) + else: + self.head = "Warning %s" % (rule) if rule else "Warning" + dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg) + + self.line = clazz.line + blame = clazz.blame + if detail is not None: + dump += "\n in " + repr(detail) + self.line = detail.line + blame = detail.blame + dump += "\n in " + repr(clazz) + dump += "\n in " + repr(clazz.pkg) + dump += "\n at line " + repr(self.line) + if blame is not None: + dump += "\n last modified by %s in %s" % (blame[1], blame[0]) + + self.dump = dump + + def __repr__(self): + return self.dump + + +failures = {} + +def _fail(clazz, detail, error, rule, msg): + """Records an API failure to be processed later.""" + global failures + + sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) + sig = sig.replace(" deprecated ", " ") + + failures[sig] = Failure(sig, clazz, detail, error, rule, msg) + + +def warn(clazz, detail, rule, msg): + _fail(clazz, detail, False, rule, msg) + +def error(clazz, detail, rule, msg): + _fail(clazz, detail, True, rule, msg) + + +if __name__ == "__main__": + next_path = sys.argv[1] + prev_path = sys.argv[2] + + next_api = _parse_stream_path(next_path) + prev_api = _parse_stream_path(prev_path) + + # Remove all existing things so we're left with new + for prev_clazz in prev_api.values(): + if prev_clazz.fullname not in next_api: continue + cur_clazz = next_api[prev_clazz.fullname] + + sigs = { i.ident: i for i in prev_clazz.ctors } + cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.methods } + cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ] + sigs = { i.ident: i for i in prev_clazz.fields } + cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ] + + # Forget about class entirely when nothing new + if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0: + del next_api[prev_clazz.fullname] + + for clazz in next_api.values(): + if "@Deprecated " in clazz.raw and not clazz.fullname in prev_api: + error(clazz, None, None, "Found API deprecation at birth") + + if "@Deprecated " in clazz.raw: continue + + for i in clazz.ctors + clazz.methods + clazz.fields: + if "@Deprecated " in i.raw: + error(clazz, i, None, "Found API deprecation at birth " + i.ident) + + print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), + format(reset=True))) + for f in sorted(failures): + print failures[f] + print diff --git a/wifi/OWNERS b/wifi/OWNERS index 2caf9ed7a38a..777df571fd62 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:/OWNERS |